-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
Improve flexibility of Slack connector #4812
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks a lot for the slack connector improvement 🚀
I added a couple suggestions and I think we need to take a closer look at that channel name being stored in the input channel.
rasa/core/channels/slack.py
Outdated
or is_app_mention(output) | ||
or channel == self.slack_channel | ||
): | ||
self.set_output_channel(channel) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we can't change the channel of the slack input. it is shared between all the async incoming messages so you might change it while another request is also getting processed (this problem might not surface right now, if the asyncs & awaits are at the right )
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should remove that object state on the input channel and directly pass it to the output channel
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tmbo , I am using the metadata object to store this value as well as others related to a specific user request. I think we'll need to explore incorporating the new converation session feature from 1.6.0.
rasa/core/channels/slack.py
Outdated
@@ -109,6 +109,20 @@ def _get_text_from_slack_buttons(buttons): | |||
return super(SlackBot, self).api_call("chat.postMessage", **json_message) | |||
|
|||
|
|||
def is_app_mention(user_message) -> bool: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please add tests for these as well as type annotations for the parameters & a doc string
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added the type annotations and standardized these with the other functions in the SlackInput channel. However, I'm not certain how to proceed with the test cases as the current channel tests for slack do not have a token. Is there a mock testing kit you'd prefer that I use for these?
@kearnsw are you still planning to move forward with this PR? |
@erohmensing: Yes, I am not intimately familiar with the way channels work so I need to spend some time reviewing that part of the codebase as @tmbo suggests. |
@kearnsw Let us know if you have any questions or need feedback :-) |
There was a typing issue that slipped in when I made the prior updates. I've uploaded the latest after testing everything with pytype and formatted the code with black. @wochinge, can you provide feedback on the updates? I have taken @tmbo's suggestion and am no longer setting the output channel property, and instead using the metadata parameter to store request specific information. The current implementation will leave the output channel configured static, while allowing for async messages based on the request from the sender. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kearnsw Thanks for your great work! Left a bit of feedback, but already looks great 🎉
rasa/core/channels/slack.py
Outdated
event = slack_event.get("event") | ||
|
||
metadata = {} | ||
metadata["out_channel"] = event.get("channel") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just checked https://api.slack.com/types/event and there the channel
param is part of item
? Would also be awesome to add the link to https://api.slack.com/types/event as a comment in the code
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's odd. This is the event object that I receive from the slack connector:
{ 'client_msg_id': 'XXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX,
'type': 'message',
'text': 'hi',
'user': 'XXXXXXXXX',
'ts': '1579802617.000800',
'team': 'XXXXXXXXX',
'blocks': [{'type': 'rich_text', 'block_id': 'XXXXX', 'elements': [{'type': 'rich_text_section', 'elements': [{'type': 'text', 'text': 'hi'}]}]}],
'channel': 'XXXXXXXXX',
'event_ts': '1579802617.000800',
'channel_type': 'im'
}
rasa/core/channels/slack.py
Outdated
output.get("event").get("user"), | ||
metadata, | ||
) | ||
if ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what do we need this condition for?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a holdover from the original codebase. I added the checks for direct message and app mention events. I think the response text should probably be set to "Received message on unsupported channel" or something to that effect instead of a blank response.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok 👍 But then let's move it up to the condition in line 386
right? you could also write a helper function is_supported_channel(output, metadata)
and then have a condition like that:
elif self_is_user_message(output) and self._is_supported_channel(output):
...
else:
logger.warning(f"Received message on unsupported channel: {metadata["out_channel"]}")
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would rather log the "unsupport channel" case instead of returning it as part response to the HTTP request, because Slack is just throwing away this message, while logger.warning(...)
will show it to the user.
rasa/core/channels/slack.py
Outdated
@@ -311,6 +329,18 @@ def _get_interactive_response(action: Dict) -> Optional[Text]: | |||
|
|||
return response.text("") | |||
|
|||
def get_metadata(self, request: Request): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we please unit test this method?
@wochinge , can you advise on how to unit test the slack connector, since this requires API credentials. I do not see a precedent in the test suite currently. Thank you! |
@kearnsw Which methods do you want to unit test? I suggest targeting the methods individually so you don't have to deal with any actual API request (e.g. |
I can proceed that way. I wasn't sure if there would be interest in setting up the CI/CD pipeline with environment variables for testing the Slack interface, in case there are breaking changes to the Slack API. Thanks for the feedback, @wochinge! |
Co-Authored-By: Tobias Wochinger <mail@tobias-wochinger.de>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your work and great documentation. Really appreciate it 🎉
Could you please also add a changelog? Thanks!
rasa/core/channels/slack.py
Outdated
request: a `Request` object that contains a slack API event in the body. | ||
|
||
Returns: | ||
A `dict` containing the output channel for the response, the text from the user, the sender's ID, and |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's avoid repeating the type
in the docstring. Makes it harder to maintain if we ever change the return types. Note that you probably have to reformat my suggestion cause it's too long for a single line.
A `dict` containing the output channel for the response, the text from the user, the sender's ID, and | |
Metadata extracted from the sent event payload. This includes the output channel for the response, the text from the user and users that have installed the bot. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kearnsw Have you seen that one?
rasa/core/channels/slack.py
Outdated
output.get("event").get("user"), | ||
metadata, | ||
) | ||
if ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok 👍 But then let's move it up to the condition in line 386
right? you could also write a helper function is_supported_channel(output, metadata)
and then have a condition like that:
elif self_is_user_message(output) and self._is_supported_channel(output):
...
else:
logger.warning(f"Received message on unsupported channel: {metadata["out_channel"]}")
rasa/core/channels/slack.py
Outdated
output.get("event").get("user"), | ||
metadata, | ||
) | ||
if ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would rather log the "unsupport channel" case instead of returning it as part response to the HTTP request, because Slack is just throwing away this message, while logger.warning(...)
will show it to the user.
rasa/core/channels/slack.py
Outdated
def get_output_channel(self) -> OutputChannel: | ||
return SlackBot(self.slack_token, self.slack_channel) | ||
def get_output_channel(self, channel: Optional[Text] = None) -> OutputChannel: | ||
if channel is not None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
optional: I'd turn it around (if is None else ...
) because it's a bit easier to read (you don't have to negate in your head), but that's really just a personal tase
@@ -462,6 +462,50 @@ def test_botframework_attachments(): | |||
assert ch.add_attachments_to_metadata(payload, metadata) == updated_metadata | |||
|
|||
|
|||
def test_slack_metadata(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for adding the test 🚀
Co-Authored-By: Tobias Wochinger <mail@tobias-wochinger.de>
rasa/core/channels/slack.py
Outdated
@@ -411,7 +411,8 @@ def blueprint( | |||
return slack_webhook | |||
|
|||
def get_output_channel(self, channel: Optional[Text] = None) -> OutputChannel: | |||
if channel is not None: | |||
channel = channel or self.slack_channel | |||
return SlackBot(self.slack_token, channel) | |||
return SlackBot(self.slack_token, channel) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should still delete the lines after
CHANGELOG.rst
Outdated
@@ -54,6 +54,11 @@ Features | |||
``intent_tokenization_flag``: Flag to check whether to split intents (default ``False``). | |||
``intent_split_symbol``: Symbol on which intent should be split (default ``_``) | |||
|
|||
- `#4811 <https://github.com/RasaHQ/rasa/issues/4811>`_: Support invoking a ``SlackBot`` by direct messaging or @ mentions. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kearnsw Please don't edit the Changelog.rst
manually. Instead, create a file 4812.improvement.rst
in the changelog
directory (the process is also described in here https://github.com/RasaHQ/rasa/blob/master/changelog/README.md )
docs/user-guide/connectors/slack.rst
Outdated
the argument to post DMs to the bot. | ||
- The ``slack_channel`` is a channel that the bot will listen to for messages | ||
in addition to direct messages and app mentions, i.e. "@app_name". | ||
This can be a channel or an individual person. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what do you mean by "This can be a channel or an individual person."?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@wochinge, this is from the original documentation. I added the part about "in addition to direct messages and app mentions, i.e. "@app_name". I'll try to clarify this sentence overall.
tests/core/test_channels.py
Outdated
slack_token="YOUR_SLACK_TOKEN", slack_channel="YOUR_SLACK_CHANNEL" | ||
) | ||
|
||
r = Request(None, None, None, None, None, app="test") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we recently discovered that this will lead to an error when we upgrade dependencies, let's rather do:
r = Request(None, None, None, None, None, app="test") | |
r = Mock() | |
r.json = direct_message_event |
The build is failing on tests unrelated to the modified code. Can you advise @wochinge ? |
Hi @kearnsw , can you please rebase to / merge in master ? We switched to GitHub actions which are more reliable and also faster. |
CHANGELOG.rst
Outdated
@@ -1,8 +1,8 @@ | |||
:desc: Rasa Open Source Changelog | |||
:desc: Rasa OSS Changelog |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you please undo this change?
CHANGELOG.rst
Outdated
|
||
|
||
Rasa Open Source Change Log | ||
=========================== | ||
Rasa OSS Change Log |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you please undo this change?
CHANGELOG.rst
Outdated
@@ -17,89 +17,6 @@ This project adheres to `Semantic Versioning`_ starting with version 1.0. | |||
|
|||
.. towncrier release notes start | |||
|
|||
[1.7.0] - 2020-01-29 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you please undo this change?
changelog/4811.improvement.rst
Outdated
@@ -0,0 +1 @@ | |||
Support invoking a ``SlackBot`` by direct messaging or @ mentions. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Support invoking a ``SlackBot`` by direct messaging or @ mentions. | |
Support invoking a ``SlackBot`` by direct messaging or ``@<app name>`` mentions. |
rasa/core/channels/slack.py
Outdated
event = slack_event.get("event", {}) | ||
return { | ||
"out_channel": event.get("channel"), | ||
"text": event.get("text"), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it feels a bit weird that the metadata
also returns sender
and text
. The metadata is meant to provide additional information around a message. text
and sender
are the message. So in my opinion it would be a bit cleaner to keep this separated, what you think?
@wochinge, looks like the build environment errored out. |
Co-Authored-By: Tobias Wochinger <mail@tobias-wochinger.de>
@kearnsw Can you please update with the latest master again? |
@kearnsw I'm sorry, but you have to merge in the latest master state again 🙈 |
Hi @wochinge , I’m not understanding the errors for the past few merge builds. Are these backend issues or problems with the submitted code? |
Looks all good 👍 |
Proposed changes:
By default:
Previously, this functionality was either impossible or difficult for a new user to implement. See full description in #4811
Status (please check what you already did):
black
(please check Readme for instructions)