Skip to content

Commit 6e8ee27

Browse files
authored
Add AsyncApp slackapi#2 (slackapi#24)
* Add AsyncApp
1 parent 333db2f commit 6e8ee27

100 files changed

Lines changed: 8877 additions & 736 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

pytest.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ log_format = %(asctime)s %(levelname)s %(message)s
55
log_date_format = %Y-%m-%d %H:%M:%S
66
filterwarnings =
77
ignore:"@coroutine" decorator is deprecated since Python 3.8, use "async def" instead:DeprecationWarning
8+
ignore:The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.:DeprecationWarning

samples/aiohttp/.gitkeep

Whitespace-only changes.

samples/async_app.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# ------------------------------------------------
2+
# instead of slack_bolt in requirements.txt
3+
import sys
4+
5+
sys.path.insert(1, "../src")
6+
# ------------------------------------------------
7+
8+
import logging
9+
10+
logging.basicConfig(level=logging.DEBUG)
11+
12+
from slack_bolt import AsyncApp
13+
14+
app = AsyncApp()
15+
16+
17+
@app.middleware # or app.use(log_request)
18+
async def log_request(logger, payload, next):
19+
logger.debug(payload)
20+
return await next()
21+
22+
23+
@app.event("app_mention")
24+
async def event_test(payload, say, logger):
25+
logger.info(payload)
26+
await say("What's up?")
27+
28+
29+
if __name__ == '__main__':
30+
app.start(3000)
31+
32+
# pip install slackclient
33+
# pip install -i https://test.pypi.org/simple/ slack_bolt
34+
# export SLACK_SIGNING_SECRET=***
35+
# export SLACK_BOT_TOKEN=xoxb-***
36+
# python app.py

samples/aws_lambda/aws_lambda.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,6 @@
66
# ------------------------------------------------
77

88
import logging
9-
10-
logging.basicConfig(format='%(asctime)s %(message)s', level=logging.DEBUG)
11-
129
from slack_bolt import App
1310
from slack_bolt.adapter.aws_lambda import SlackRequestHandler
1411

@@ -22,6 +19,10 @@ def handle_app_mentions(payload, say, logger):
2219
say("What's up?")
2320

2421

22+
SlackRequestHandler.clear_all_log_handlers()
23+
logging.basicConfig(format='%(asctime)s %(message)s', level=logging.DEBUG)
24+
25+
2526
def handler(event, context):
2627
slack_handler = SlackRequestHandler(app=app)
2728
return slack_handler.handle(event, context)

samples/aws_lambda/aws_lambda_oauth.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,6 @@
66
# ------------------------------------------------
77

88
import logging
9-
10-
logging.basicConfig(format='%(asctime)s %(message)s', level=logging.DEBUG)
11-
129
from slack_bolt import App
1310
from slack_bolt.adapter.aws_lambda import SlackRequestHandler, LambdaS3OAuthFlow
1411

@@ -25,6 +22,10 @@ def handle_app_mentions(payload, say, logger):
2522
say("What's up?")
2623

2724

25+
SlackRequestHandler.clear_all_log_handlers()
26+
logging.basicConfig(format='%(asctime)s %(message)s', level=logging.DEBUG)
27+
28+
2829
def handler(event, context):
2930
slack_handler = SlackRequestHandler(app=app)
3031
return slack_handler.handle(event, context)

samples/oauth_app.py

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,12 @@
1111
logging.basicConfig(level=logging.DEBUG)
1212
app = App()
1313

14-
def log_request(logger, payload, next):
15-
logger.debug(payload)
16-
return next()
17-
18-
app.use(log_request)
1914

2015
@app.event("app_mention")
21-
def handle_app_mentions(payload, say, logger):
16+
def event_test(payload, say, logger):
2217
logger.info(payload)
2318
say("What's up?")
2419

25-
@app.event("message")
26-
def handle_messages():
27-
pass
2820

2921
if __name__ == '__main__':
3022
app.start(3000)

setup.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@
2323
package_dir={"": "src"},
2424
py_modules=[splitext(basename(path))[0] for path in glob('src/*.py')],
2525
setup_requires=["pytest-runner==5.2"],
26-
tests_require=["pytest==3.8.2"],
26+
tests_require=[
27+
"pytest>=5,<6",
28+
"pytest-asyncio<1",
29+
],
2730
install_requires=[
2831
"slackclient>=2,<3", # TODO: will be replaced with slack_sdk==3.0.0
2932
"aiohttp>=3,<4", # slackclient depends on aiohttp

src/slack_bolt/__init__.py

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,20 @@
1-
from .app import App
2-
from .context import BoltContext
3-
from .context.ack import Ack
4-
from .context.respond import Respond
5-
from .context.say import Say
6-
from .kwargs_injection import Args
7-
from .listener import Listener
8-
from .listener_matcher import CustomListenerMatcher
9-
from .request import BoltRequest
10-
from .response import BoltResponse
1+
from .app import App # noqa
2+
from .app.async_app import AsyncApp # noqa
3+
from .context import BoltContext # noqa
4+
from .context.ack import Ack # noqa
5+
from .context.ack.async_ack import AsyncAck # noqa
6+
from .context.async_context import AsyncBoltContext # noqa
7+
from .context.respond import Respond # noqa
8+
from .context.respond.async_respond import AsyncRespond # noqa
9+
from .context.say import Say # noqa
10+
from .context.say.async_say import AsyncSay # noqa
11+
from .kwargs_injection import Args # noqa
12+
from .kwargs_injection import Args # noqa
13+
from .listener import Listener # noqa
14+
from .listener.async_listener import AsyncListener # noqa
15+
from .listener_matcher import CustomListenerMatcher # noqa
16+
from .listener_matcher import CustomListenerMatcher # noqa
17+
from .request import BoltRequest # noqa
18+
from .request.async_request import AsyncBoltRequest # noqa
19+
from .response import BoltResponse # noqa
20+
from .response import BoltResponse # noqa
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import re
2+
3+
from aiohttp import web
4+
5+
from slack_bolt.request.async_request import AsyncBoltRequest
6+
from slack_bolt.response import BoltResponse
7+
8+
9+
async def to_bolt_request(request: web.Request) -> AsyncBoltRequest:
10+
return AsyncBoltRequest(
11+
body=await request.text(),
12+
query=request.query_string,
13+
headers=request.headers,
14+
)
15+
16+
17+
async def to_aiohttp_response(bolt_resp: BoltResponse) -> web.Response:
18+
content_type = bolt_resp.headers.pop(
19+
"content-type",
20+
["application/json" if bolt_resp.body.startswith("{") else "text/plain"]
21+
)[0]
22+
content_type = re.sub(r";\s*charset=utf-8", "", content_type)
23+
resp = web.Response(
24+
status=bolt_resp.status,
25+
body=bolt_resp.body,
26+
headers=bolt_resp.first_headers_without_set_cookie(),
27+
content_type=content_type,
28+
)
29+
for cookie in bolt_resp.cookies():
30+
for name, c in cookie.items():
31+
resp.set_cookie(
32+
name=name,
33+
value=c.value,
34+
max_age=c.get("max-age", None),
35+
expires=c.get("expires", None),
36+
path=c.get("path", None),
37+
domain=c.get("domain", None),
38+
secure=True,
39+
httponly=True,
40+
)
41+
return resp

src/slack_bolt/adapter/aws_lambda/handler.py

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,27 @@
11
import base64
22
import logging
3-
from typing import List
3+
from typing import List, Dict, Optional
44

55
from slack_bolt.app import App
66
from slack_bolt.logger import get_bolt_app_logger
77
from slack_bolt.oauth import OAuthFlow
88
from slack_bolt.request import BoltRequest
99
from slack_bolt.response import BoltResponse
1010

11-
# https://stackoverflow.com/questions/37703609/using-python-logging-with-aws-lambda
12-
root = logging.getLogger()
13-
if root.handlers:
14-
for handler in root.handlers:
15-
root.removeHandler(handler)
16-
1711

1812
class SlackRequestHandler():
1913
def __init__(self, app: App):
2014
self.app = app
2115
self.logger = get_bolt_app_logger(app.name, SlackRequestHandler)
2216

17+
@classmethod
18+
def clear_all_log_handlers(cls):
19+
# https://stackoverflow.com/questions/37703609/using-python-logging-with-aws-lambda
20+
root = logging.getLogger()
21+
if root.handlers:
22+
for handler in root.handlers:
23+
root.removeHandler(handler)
24+
2325
def handle(self, event, context):
2426
self.logger.debug(f"Incoming event: {event}, context: {context}")
2527

@@ -29,11 +31,13 @@ def handle(self, event, context):
2931
if method == "GET":
3032
if self.app.oauth_flow is not None:
3133
oauth_flow: OAuthFlow = self.app.oauth_flow
32-
bolt_req = to_bolt_request(event)
34+
bolt_req: BoltRequest = to_bolt_request(event)
3335
query = bolt_req.query
34-
is_callback = (query.get("code", None) is not None \
35-
and query.get("state", None) is not None) \
36-
or query.get("error", None) is not None
36+
is_callback = query is not None and (
37+
(_first_value(query, "code") is not None and _first_value(query, "state") is not None)
38+
or
39+
_first_value(query, "error") is not None
40+
)
3741
if is_callback:
3842
bolt_resp = oauth_flow.handle_callback(bolt_req)
3943
return to_aws_response(bolt_resp)
@@ -49,7 +53,15 @@ def handle(self, event, context):
4953
return not_found()
5054

5155

52-
def to_bolt_request(event):
56+
def _first_value(query: Dict[str, List[str]], name: str) -> Optional[str]:
57+
if query:
58+
values = query.get(name, [])
59+
if values and len(values) > 0:
60+
return values[0]
61+
return None
62+
63+
64+
def to_bolt_request(event) -> BoltRequest:
5365
body = event.get("body", "")
5466
if event["isBase64Encoded"]:
5567
body = base64.b64decode(body).decode("utf-8")
@@ -63,15 +75,15 @@ def to_bolt_request(event):
6375
)
6476

6577

66-
def to_aws_response(resp: BoltResponse):
78+
def to_aws_response(resp: BoltResponse) -> Dict[str, any]:
6779
return {
6880
"statusCode": resp.status,
6981
"body": resp.body,
7082
"headers": resp.first_headers(),
7183
}
7284

7385

74-
def not_found():
86+
def not_found() -> Dict[str, any]:
7587
return {
7688
"statusCode": 404,
7789
"body": "Not Found",

0 commit comments

Comments
 (0)