Skip to content

Commit

Permalink
Merge branch 'main' into issue-199-detecting-invalid-event-type
Browse files Browse the repository at this point in the history
  • Loading branch information
seratch committed Jan 14, 2021
2 parents e72be01 + a11f6d1 commit 0ebc6ca
Show file tree
Hide file tree
Showing 43 changed files with 1,793 additions and 10 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
run: |
python setup.py install
pip install -U pip
pip install "pytest>=5,<6" "pytest-cov>=2,<3"
pip install "pytest>=5,<6" "pytest-cov>=2,<3" "flask_sockets>0.2,<1"
- name: Run tests without aiohttp
run: |
pytest tests/slack_bolt/
Expand Down
2 changes: 1 addition & 1 deletion codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ coverage:
status:
project:
default:
threshold: 0.3%
threshold: 2.0%
patch:
default:
target: 50%
70 changes: 70 additions & 0 deletions docs/_basic/socket_mode.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
---
title: Using Socket Mode
lang: en
slug: socket-mode
order: 16
---

<div class="section-content">
With the introduction of [Socket Mode](https://api.slack.com/apis/connections/socket), 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.

While we recommend using [the built-in Socket Mode adapter](https://github.com/slackapi/bolt-python/tree/main/slack_bolt/adapter/socket_mode/builtin), there are a few other 3rd party library based implementations. Here is the list of available adapters.

|PyPI Project|Bolt Adapter|
|-|-|
|[slack_sdk](https://pypi.org/project/slack-sdk/)|[slack_bolt.adapter.socket_mode](https://github.com/slackapi/bolt-python/tree/main/slack_bolt/adapter/socket_mode/builtin)|
|[websocket_client](https://pypi.org/project/websocket_client/)|[slack_bolt.adapter.socket_mode.websocket_client](https://github.com/slackapi/bolt-python/tree/main/slack_bolt/adapter/socket_mode/websocket_client)|
|[aiohttp](https://pypi.org/project/aiohttp/) (asyncio-based)|[slack_bolt.adapter.socket_mode.aiohttp](https://github.com/slackapi/bolt-python/tree/main/slack_bolt/adapter/socket_mode/aiohttp)|
|[websockets](https://pypi.org/project/websockets/) (asyncio-based)|[slack_bolt.adapter.socket_mode.websockets](https://github.com/slackapi/bolt-python/tree/main/slack_bolt/adapter/socket_mode/websockets)|

</div>

```python
import os
from slack_bolt import App
from slack_bolt.adapter.socket_mode import SocketModeHandler

# Install the Slack app and get xoxb- token in advance
app = App(token=os.environ["SLACK_BOT_TOKEN"])

# Add middleware / listeners here

if __name__ == "__main__":
# export SLACK_APP_TOKEN=xapp-***
# export SLACK_BOT_TOKEN=xoxb-***
handler = SocketModeHandler(app, os.environ["SLACK_APP_TOKEN"])
handler.start()
```

<details class="secondary-wrapper">
<summary markdown="0">
<h4 class="secondary-header">Using Async</h4>
</summary>

<div class="secondary-content" markdown="0">
To use the asyncio-based adapters such as aiohttp, your 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](https://slack.dev/bolt-python/concepts#async) document and relevant [examples](https://github.com/slackapi/bolt-python/tree/main/examples).
</div>

```python
from slack_bolt.app.async_app import AsyncApp
# The default is the aiohttp based implementation
from slack_bolt.adapter.socket_mode.async_handler import AsyncSocketModeHandler

app = AsyncApp(token=os.environ["SLACK_BOT_TOKEN"])

# Add middleware / listeners here

async def main():
handler = AsyncSocketModeHandler(app, os.environ["SLACK_APP_TOKEN"])
await handler.start_async()

if __name__ == "__main__":
import asyncio
asyncio.run(main())
```

</details>
102 changes: 102 additions & 0 deletions examples/socket_mode.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# ------------------------------------------------
# instead of slack_bolt in requirements.txt
import sys

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

import logging

logging.basicConfig(level=logging.DEBUG)

import os

from slack_bolt import App
from slack_bolt.adapter.socket_mode import SocketModeHandler

# Install the Slack app and get xoxb- token in advance
app = App(token=os.environ["SLACK_BOT_TOKEN"])


@app.command("/hello-socket-mode")
def hello_command(ack, body):
user_id = body["user_id"]
ack(f"Hi <@{user_id}>!")


@app.event("app_mention")
def event_test(event, say):
say(f"Hi there, <@{event['user']}>!")


def ack_shortcut(ack):
ack()


def open_modal(body, client):
client.views_open(
trigger_id=body["trigger_id"],
view={
"type": "modal",
"callback_id": "socket_modal_submission",
"submit": {"type": "plain_text", "text": "Submit",},
"close": {"type": "plain_text", "text": "Cancel",},
"title": {"type": "plain_text", "text": "Socket Modal",},
"blocks": [
{
"type": "input",
"block_id": "q1",
"label": {"type": "plain_text", "text": "Write anything here!",},
"element": {"action_id": "feedback", "type": "plain_text_input",},
},
{
"type": "input",
"block_id": "q2",
"label": {
"type": "plain_text",
"text": "Can you tell us your favorites?",
},
"element": {
"type": "external_select",
"action_id": "favorite-animal",
"min_query_length": 0,
"placeholder": {
"type": "plain_text",
"text": "Select your favorites",
},
},
},
],
},
)


app.shortcut("socket-mode")(ack=ack_shortcut, lazy=[open_modal])


all_options = [
{"text": {"type": "plain_text", "text": ":cat: Cat"}, "value": "cat",},
{"text": {"type": "plain_text", "text": ":dog: Dog"}, "value": "dog",},
{"text": {"type": "plain_text", "text": ":bear: Bear"}, "value": "bear",},
]


@app.options("favorite-animal")
def external_data_source_handler(ack, body):
keyword = body.get("value")
if keyword is not None and len(keyword) > 0:
options = [o for o in all_options if keyword in o["text"]["text"]]
ack(options=options)
else:
ack(options=all_options)


@app.view("socket_modal_submission")
def submission(ack):
ack()


if __name__ == "__main__":
# export SLACK_APP_TOKEN=xapp-***
# export SLACK_BOT_TOKEN=xoxb-***
SocketModeHandler(app, os.environ["SLACK_APP_TOKEN"]).start()
112 changes: 112 additions & 0 deletions examples/socket_mode_async.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# ------------------------------------------------
# instead of slack_bolt in requirements.txt
import sys


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

import logging

logging.basicConfig(level=logging.DEBUG)

import os

from slack_bolt.app.async_app import AsyncApp
from slack_bolt.adapter.socket_mode.async_handler import AsyncSocketModeHandler

# Install the Slack app and get xoxb- token in advance
app = AsyncApp(token=os.environ["SLACK_BOT_TOKEN"])


@app.command("/hello-socket-mode")
async def hello_command(ack, body):
user_id = body["user_id"]
await ack(f"Hi <@{user_id}>!")


@app.event("app_mention")
async def event_test(event, say):
await say(f"Hi there, <@{event['user']}>!")


async def ack_shortcut(ack):
await ack()


async def open_modal(body, client):
await client.views_open(
trigger_id=body["trigger_id"],
view={
"type": "modal",
"callback_id": "socket_modal_submission",
"submit": {"type": "plain_text", "text": "Submit",},
"close": {"type": "plain_text", "text": "Cancel",},
"title": {"type": "plain_text", "text": "Socket Modal",},
"blocks": [
{
"type": "input",
"block_id": "q1",
"label": {"type": "plain_text", "text": "Write anything here!",},
"element": {"action_id": "feedback", "type": "plain_text_input",},
},
{
"type": "input",
"block_id": "q2",
"label": {
"type": "plain_text",
"text": "Can you tell us your favorites?",
},
"element": {
"type": "external_select",
"action_id": "favorite-animal",
"min_query_length": 0,
"placeholder": {
"type": "plain_text",
"text": "Select your favorites",
},
},
},
],
},
)


app.shortcut("socket-mode")(ack=ack_shortcut, lazy=[open_modal])


all_options = [
{"text": {"type": "plain_text", "text": ":cat: Cat"}, "value": "cat",},
{"text": {"type": "plain_text", "text": ":dog: Dog"}, "value": "dog",},
{"text": {"type": "plain_text", "text": ":bear: Bear"}, "value": "bear",},
]


@app.options("favorite-animal")
async def external_data_source_handler(ack, body):
keyword = body.get("value")
if keyword is not None and len(keyword) > 0:
options = [o for o in all_options if keyword in o["text"]["text"]]
await ack(options=options)
else:
await ack(options=all_options)


@app.view("socket_modal_submission")
async def submission(ack):
await ack()


# export SLACK_APP_TOKEN=xapp-***
# export SLACK_BOT_TOKEN=xoxb-***


async def main():
handler = AsyncSocketModeHandler(app, os.environ["SLACK_APP_TOKEN"])
await handler.start_async()


if __name__ == "__main__":
import asyncio

asyncio.run(main())

0 comments on commit 0ebc6ca

Please sign in to comment.