Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions examples/aiohttp_devtools/async_app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# ------------------------------------------------
# instead of slack_bolt in requirements.txt
import sys

sys.path.insert(1, "../..")
# ------------------------------------------------

import logging

logging.basicConfig(level=logging.DEBUG)

from slack_bolt.async_app import AsyncApp

app = AsyncApp()


@app.event("app_mention")
async def event_test(body, say, logger):
logger.info(body)
await say("What's up?")


@app.command("/hello-bolt-python")
# or app.command(re.compile(r"/hello-.+"))(test_command)
async def command(ack, body):
user_id = body["user_id"]
await ack(f"Hi <@{user_id}>!")


def app_factory():
return app.web_app()


# pip install -r requirements.txt
# export SLACK_SIGNING_SECRET=***
# export SLACK_BOT_TOKEN=xoxb-***
# adev runserver --port 3000 --app-factory app_factory async_app.py

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check this example to learn how to use the devtool.

2 changes: 2 additions & 0 deletions examples/aiohttp_devtools/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
aiohttp>=3,<4
aiohttp-devtools>=0.13,<0.14
32 changes: 28 additions & 4 deletions slack_bolt/app/async_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
import os
from typing import Optional, List, Union, Callable, Pattern, Dict, Awaitable, Sequence

from aiohttp import web

from slack_bolt.app.async_server import AsyncSlackAppServer
from slack_bolt.listener.asyncio_runner import AsyncioListenerRunner
from slack_bolt.middleware.message_listener_matches.async_message_listener_matches import (
AsyncMessageListenerMatches,
Expand Down Expand Up @@ -225,6 +228,8 @@ def __init__(
self._init_middleware_list_done = False
self._init_async_middleware_list()

self._server: Optional[AsyncSlackAppServer] = None

def _init_async_middleware_list(self):
if self._init_middleware_list_done:
return
Expand Down Expand Up @@ -282,17 +287,36 @@ def listener_runner(self) -> AsyncioListenerRunner:
# -------------------------
# standalone server

from .async_server import AsyncSlackAppServer

def server(
self, port: int = 3000, path: str = "/slack/events"
) -> AsyncSlackAppServer:
"""Configure a web server using AIOHTTP.

:param port: The port to listen on (Default: 3000)
:param path: The path to handle request from Slack (Default: /slack/events)
:return: None
"""
if (
self._server is None
or self._server.port != port
or self._server.path != path
):
self._server = AsyncSlackAppServer(port=port, path=path, app=self,)
return self._server

def web_app(self, path: str = "/slack/events") -> web.Application:

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When you use aiohttp-devtools, specifying the port to listen on in code does not work. That's why I removed the arg in this factory method as it's so confusing if giving it here does not work.

return self.server(path=path).web_app

def start(self, port: int = 3000, path: str = "/slack/events") -> None:
"""Start a web server using AIOHTTP.

:param port: The port to listen on (Default: 3000)
:param path: The path to handle request from Slack (Default: /slack/events)
:return: None
"""
from .async_server import AsyncSlackAppServer

self.server = AsyncSlackAppServer(port=port, path=path, app=self,)
self.server.start()
self.server(port=port, path=path).start()

# -------------------------
# main dispatcher
Expand Down
34 changes: 18 additions & 16 deletions slack_bolt/app/async_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,24 @@


class AsyncSlackAppServer:
def __init__(
self, port: int, path: str, app, # AsyncApp
port: int
path: str
bolt_app: "AsyncApp" # type:ignore
web_app: web.Application

def __init__( # type:ignore
self, port: int, path: str, app: "AsyncApp", # type:ignore
):
"""Standalone AIOHTTP Web Server

Refer to AIOHTTP documents for details.
https://docs.aiohttp.org/en/stable/web.html
"""
self._port = port
self._endpoint_path = path
self._bolt_app: "AsyncApp" = app

self.port = port
self.path = path
self.bolt_app: "AsyncApp" = app
self.web_app = web.Application()
self._bolt_oauth_flow = self._bolt_app.oauth_flow
self._bolt_oauth_flow = self.bolt_app.oauth_flow
if self._bolt_oauth_flow:
self.web_app.add_routes(
[
Expand All @@ -31,13 +35,11 @@ def __init__(
self._bolt_oauth_flow.redirect_uri_path,
self.handle_get_requests,
),
web.post(self._endpoint_path, self.handle_post_requests),
web.post(self.path, self.handle_post_requests),
]
)
else:
self.web_app.add_routes(
[web.post(self._endpoint_path, self.handle_post_requests)]
)
self.web_app.add_routes([web.post(self.path, self.handle_post_requests)])

async def handle_get_requests(self, request: web.Request) -> web.Response:
oauth_flow = self._bolt_oauth_flow
Expand All @@ -56,21 +58,21 @@ async def handle_get_requests(self, request: web.Request) -> web.Response:
return web.Response(status=404)

async def handle_post_requests(self, request: web.Request) -> web.Response:
if self._endpoint_path != request.path:
if self.path != request.path:
return web.Response(status=404)

bolt_req = await to_bolt_request(request)
bolt_resp: BoltResponse = await self._bolt_app.async_dispatch(bolt_req)
bolt_resp: BoltResponse = await self.bolt_app.async_dispatch(bolt_req)
return await to_aiohttp_response(bolt_resp)

def start(self) -> None:
""" Starts a new web server process.

:return: None
"""
if self._bolt_app.logger.level > logging.INFO:
if self.bolt_app.logger.level > logging.INFO:
print("⚡️ Bolt app is running!")
else:
self._bolt_app.logger.info("⚡️ Bolt app is running!")
self.bolt_app.logger.info("⚡️ Bolt app is running!")

web.run_app(self.web_app, host="0.0.0.0", port=self._port)
web.run_app(self.web_app, host="0.0.0.0", port=self.port)
2 changes: 1 addition & 1 deletion tests/mock_web_api_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ def run(self):
self.server = HTTPServer(("localhost", 8888), self.handler)
self.test.mock_received_requests = self.handler.received_requests
self.test.server_url = "http://localhost:8888"
self.test.host, self.test._port = self.server.socket.getsockname()
self.test.host, self.test.port = self.server.socket.getsockname()
self.test.server_started.set() # threading.Event()

self.test = None
Expand Down