Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Slack init message #7368

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
2 changes: 2 additions & 0 deletions changelog/7366.feature.md
@@ -0,0 +1,2 @@
Allow to reaction of rasa chatbot to slack opening / switching window of conversation. This allows the chatbot to provide
a "welcome message" to the user.
7 changes: 7 additions & 0 deletions docs/docs/connectors/slack.mdx
Expand Up @@ -136,6 +136,13 @@ your bot and tell you about new messages. If you are running locally, you can
sending of messages, you'll be prompted to **reinstall your app** which you will
need to do. Otherwise, Slack will confirm your change with a **Success!**)

## Optional: Initial message
If you want to have the bot greet message appear every time when user opens its chat (e.g. for the first time) you want
to send some subscribe to `app_home_opened` event. This will automatically redirect to expected `init` intent in your
domain (it might be smart to handle if a user has already encountered a chatbot and not react to this event every single
time).


Your bot is now ready to go and will receive webhook notifications about new messages.
## Optional: Interactive Components
After you've completed [Sending Messages](./slack.mdx#sending-messages) and
Expand Down
37 changes: 36 additions & 1 deletion rasa/core/channels/slack.py
Expand Up @@ -235,6 +235,28 @@ def _is_user_message(slack_event: Dict[Text, Any]) -> bool:
)
and slack_event.get("event", {}).get("text")
and not slack_event.get("event", {}).get("bot_id")
) or SlackInput._is_init_message(slack_event)

@staticmethod
def _is_init_message(slack_event: Dict[Text, Any]) -> bool:
"""
This method determines if a message sent is an initial message
from Slack when the user opens up the window with chatbot conversation.

This can happen multiple times throughout the conversation and the user
needs to handle this on the chatbot side only actually invoking
init intent e.g. if a certain time (like 24 hours) have passed since
last time welcome message (=init intent) display.

By default the init intent is not invoked as this message is normally
not invoked by Slack. For this method to work it needs to be subscribed
to in event subscription.
Args:
slack_event: event from slack saying that user has started chatbot interaction
"""
return (
slack_event.get("event") is not None
and slack_event.get("event", {}).get("type") == "app_home_opened"
)

@staticmethod
Expand Down Expand Up @@ -297,6 +319,7 @@ def _is_interactive_message(payload: Dict) -> bool:
"channels_select",
"overflow",
"datepicker",
"multi_static_select",
]
if payload.get("actions"):
action_type = payload["actions"][0].get("type")
Expand All @@ -309,6 +332,13 @@ def _is_interactive_message(payload: Dict) -> bool:
)
return False

@staticmethod
def _get_text_from_multi_select(action: Dict) -> Optional[Text]:
values = []
for val in action.get("selected_options"):
values.append(val.get("value"))
return ",".join(values)

@staticmethod
def _get_interactive_response(action: Dict) -> Optional[Text]:
"""Parse the payload for the response value."""
Expand All @@ -331,6 +361,8 @@ def _get_interactive_response(action: Dict) -> Optional[Text]:
return action.get("selected_option", {}).get("value")
elif action["type"] == "datepicker":
return action.get("selected_date")
elif action["type"] == "multi_static_select":
return SlackInput._get_text_from_multi_select(action)

async def process_message(
self,
Expand Down Expand Up @@ -519,7 +551,9 @@ async def webhook(request: Request) -> HTTPResponse:
"a user message. Skipping message."
)
return response.text("Bot message delivered.")

if self._is_init_message(output):
logger.debug("Init message recieved - sending to /init intent")
user_message = "/init"
if not self._is_supported_channel(output, metadata):
logger.warning(
f"Received message on unsupported channel: {metadata['out_channel']}"
Expand Down Expand Up @@ -564,6 +598,7 @@ def _is_supported_channel(self, slack_event: Dict, metadata: Dict) -> bool:
self._is_direct_message(slack_event)
or self._is_app_mention(slack_event)
or metadata["out_channel"] == self.slack_channel
or self._is_init_message(slack_event)
)

def get_output_channel(
Expand Down
14 changes: 14 additions & 0 deletions tests/core/channels/test_slack.py
Expand Up @@ -356,6 +356,20 @@ def test_is_slack_message_false():
assert SlackInput._is_user_message(slack_message) is False


def test_is_slack_message_true_init():

event = {
"type": "app_home_opened",
"user": "UFDM2MG77",
"channel": "A01G7854KUU",
"tab": "messages",
"event_ts": "1606315650.276505",
}
payload = json.dumps({"event": event})
slack_message = json.loads(payload)
assert SlackInput._is_user_message(slack_message) is True


def test_slackbot_init_one_parameter():
from rasa.core.channels.slack import SlackBot

Expand Down