Skip to content

Commit

Permalink
removed team and wrapper of mattermost
Browse files Browse the repository at this point in the history
  • Loading branch information
tmbo committed Feb 20, 2020
1 parent 23af1a3 commit cedd4c3
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 87 deletions.
1 change: 0 additions & 1 deletion docs/user-guide/connectors/mattermost.rst
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ you need to supply a ``credentials.yml`` with the following content:
mattermost:
url: "https://chat.example.com/api/v4"
team: "community"
token: "xxxxx" # the token for the bot account from creating the bot step.
webhook_url: "https://server.example.com/webhooks/mattermost/webhook"
Expand Down
1 change: 1 addition & 0 deletions rasa/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
DOCS_URL_DOMAINS = DOCS_BASE_URL + "/core/domains/"
DOCS_URL_STORIES = DOCS_BASE_URL + "/core/stories/"
DOCS_URL_ACTIONS = DOCS_BASE_URL + "/core/actions/"
DOCS_URL_CONNECTORS = DOCS_BASE_URL + "/user-guide/connectors/"
DOCS_URL_EVENT_BROKERS = DOCS_BASE_URL + "/api/event-brokers/"
DOCS_URL_PIPELINE = DOCS_BASE_URL + "/nlu/choosing-a-pipeline/"
DOCS_URL_COMPONENTS = DOCS_BASE_URL + "/nlu/components/"
Expand Down
2 changes: 1 addition & 1 deletion rasa/core/channels/channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ def blueprint(
raise NotImplementedError("Component listener needs to provide blueprint.")

@classmethod
def raise_missing_credentials_exception(cls) -> None:
def raise_missing_credentials_exception(cls) -> NoReturn:
raise Exception(
"To use the {} input channel, you need to "
"pass a credentials file using '--credentials'. "
Expand Down
184 changes: 103 additions & 81 deletions rasa/core/channels/mattermost.py
Original file line number Diff line number Diff line change
@@ -1,72 +1,92 @@
import json

import logging
from mattermostwrapper import MattermostAPI
import requests
from requests import Response
from sanic import Blueprint, response
from sanic.request import Request
from typing import Text, Dict, Any, List, Callable, Awaitable, Optional

from rasa.constants import DOCS_URL_CONNECTORS
from rasa.core.channels.channel import UserMessage, OutputChannel, InputChannel
from sanic.response import HTTPResponse

from rasa.utils.common import raise_warning

logger = logging.getLogger(__name__)


class MattermostBot(MattermostAPI, OutputChannel):
class MattermostBot(OutputChannel):
"""A Mattermost communication channel"""

@classmethod
def name(cls) -> Text:
return "mattermost"

@classmethod
def from_credentials(cls, credentials: Optional[Dict[Text, Any]]) -> OutputChannel:
if not credentials:
cls.raise_missing_credentials_exception()
def token_from_login(cls, url: Text, user: Text, password: Text) -> Optional[Text]:
"""Retrieve access token for mattermost user."""

return cls(credentials.get("webhook_url")) # pytype: disable=attribute-error
data = {"login_id": user, "password": password}
r = requests.post(url + "/users/login", data=json.dumps(data))
if r.status_code == 200:
return r.headers["Token"]
else:
logger.error(f"Failed to login mattermost user {user}. Response: {r}")
return None

def __init__(
self,
url: Text,
team: Text,
token: Text,
bot_channel: Text,
webhook_url: Optional[Text],
self, url: Text, token: Text, bot_channel: Text, webhook_url: Optional[Text],
) -> None:
self.url = url
self.team = team
self.token = token
self.bot_channel = bot_channel
self.webhook_url = webhook_url

super().__init__(url, team, token=token)
super(MattermostBot, self).__init__()

def _post_message_to_channel(self, channel_id: Text, message: Text):
return self._post_data_to_channel(
{"channel_id": channel_id, "message": message}
)

def _post_data_to_channel(self, data) -> Response:
"""Send a message to a mattermost channel."""

headers = {"Authorization": "Bearer " + self.token}
r = requests.post(self.url + "/posts", headers=headers, data=json.dumps(data))
if not r.status_code == 200:
logger.error(
f"Failed to send message to mattermost channel "
f"{data.get('channel_id')}. Response: {r}"
)
return r

async def send_text_message(
self, recipient_id: Text, text: Text, **kwargs: Any
) -> None:
for message_part in text.split("\n\n"):
self.post_channel(self.bot_channel, message_part)
self._post_message_to_channel(self.bot_channel, message_part)

async def send_custom_json(
self, recipient_id: Text, json_message: Dict[Text, Any], **kwargs: Any
) -> None:
json_message.setdefault("channel_id", self.bot_channel)
json_message.setdefault("message", "")
self.post("/posts", json_message)

self._post_data_to_channel(json_message)

async def send_image_url(
self, recipient_id: Text, image: Text, **kwargs: Any
) -> None:
"""Sends an image."""
image_url = image

props = {"attachments": []}
props["attachments"].append({"image_url": image_url})

json_message = {}
json_message.setdefault("channel_id", self.bot_channel)
json_message.setdefault("props", props)

self.post("/posts", json_message)
self._post_data_to_channel(
{
"channel_id": self.bot_channel,
"props": {"attachments": [{"image_url": image}]},
}
)

async def send_text_with_buttons(
self,
Expand All @@ -79,26 +99,23 @@ async def send_text_with_buttons(

# buttons are a list of objects: [(option_name, payload)]
# See https://docs.mattermost.com/developer/interactive-messages.html#message-buttons
button_block = {"actions": []}
for button in buttons:
button_block["actions"].append(
{
"name": button["title"],
"integration": {
"url": self.webhook_url,
"context": {"action": button["payload"]},
},
}
)
props = {"attachments": []}
props["attachments"].append(button_block)

json_message = {}
json_message.setdefault("channel_id", self.bot_channel)
json_message.setdefault("message", text)
json_message.setdefault("props", props)

self.post("/posts", json_message)
actions = [
{
"name": button["title"],
"integration": {
"url": self.webhook_url,
"context": {"action": button["payload"]},
},
}
for button in buttons
]

props = {"attachments": [{"actions": actions}]}

self._post_data_to_channel(
{"channel_id": self.bot_channel, "message": text, "props": props}
)


class MattermostInput(InputChannel):
Expand All @@ -110,34 +127,42 @@ def name(cls) -> Text:

@classmethod
def from_credentials(cls, credentials: Optional[Dict[Text, Any]]) -> InputChannel:
if not credentials:
if credentials is None:
cls.raise_missing_credentials_exception()

# pytype: disable=attribute-error
return cls(
credentials.get("url"),
credentials.get("team"),
credentials.get("token"),
credentials.get("webhook_url"),
)
if credentials.get("pw") is not None or credentials.get("user") is not None:
raise_warning(
"Mattermost recently switched to bot accounts. 'user' and 'pw' "
"should not be used anymore, you should rather convert your "
"account to a bot account and use a token. Password based "
"authentication will be removed in Rasa Open Source 2.0.",
FutureWarning,
docs=DOCS_URL_CONNECTORS + "mattermost/",
)
token = MattermostBot.token_from_login(
credentials.get("url"), credentials.get("user"), credentials.get("pw")
)
else:
token = credentials.get("token")

return cls(credentials.get("url"), token, credentials.get("webhook_url"),)
# pytype: enable=attribute-error

def __init__(self, url: Text, team: Text, token: Text, webhook_url: Text) -> None:
def __init__(self, url: Text, token: Text, webhook_url: Text) -> None:
"""Create a Mattermost input channel.
Needs a couple of settings to properly authenticate and validate
messages.
Args:
url: Your Mattermost team url including /v4 example
https://mysite.example.com/api/v4
team: Your mattermost team name
token: Your mattermost bot token
webhook_url: The mattermost callback url as specified
in the outgoing webhooks in mattermost example
https://mysite.example.com/webhooks/mattermost/webhook
"""
self.url = url
self.team = team
self.token = token
self.webhook_url = webhook_url

Expand All @@ -149,27 +174,15 @@ async def message_with_trigger_word(
) -> None:
# splitting to get rid of the @botmention
# trigger we are using for this
text = output["text"].split(" ", 1)
text = text[1]

sender_id = output["user_id"]
self.bot_channel = output["channel_id"]

try:
out_channel = MattermostBot(
self.url, self.team, self.token, self.bot_channel, self.webhook_url,
)
user_msg = UserMessage(
text,
out_channel,
sender_id,
input_channel=self.name(),
metadata=metadata,
)
await on_new_message(user_msg)
except Exception as e:
logger.error(f"Exception when trying to handle message.{e}")
logger.debug(e, exc_info=True)
split_message = output["text"].split(" ", 1)
if len(split_message) >= 2:
message = split_message[1]
else:
message = output["text"]

await self._handle_message(
message, output["user_id"], output["channel_id"], metadata, on_new_message
)

async def action_from_button(
self,
Expand All @@ -180,21 +193,30 @@ async def action_from_button(
# get the action, the buttons triggers
action = output["context"]["action"]

sender_id = output["user_id"]
self.bot_channel = output["channel_id"]
await self._handle_message(
action, output["user_id"], output["channel_id"], metadata, on_new_message
)

async def _handle_message(
self,
message: Text,
sender_id: Text,
bot_channel: Text,
metadata: Optional[Dict],
on_new_message: Callable[[UserMessage], Awaitable[None]],
):
try:
out_channel = MattermostBot(
self.url, self.team, self.token, self.bot_channel, self.webhook_url,
self.url, self.token, bot_channel, self.webhook_url,
)
context_action = UserMessage(
action,
user_msg = UserMessage(
message,
out_channel,
sender_id,
input_channel=self.name(),
metadata=metadata,
)
await on_new_message(context_action)
await on_new_message(user_msg)
except Exception as e:
logger.error(f"Exception when trying to handle message.{e}")
logger.debug(e, exc_info=True)
Expand Down
1 change: 0 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ slackclient==1.3.1
python-telegram-bot==11.1.0
twilio==6.26.3
webexteamssdk==1.1.1
mattermostwrapper==2.2
rocketchat_API==0.6.31
colorhash==1.0.2
pika==1.0.1
Expand Down
1 change: 0 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@
"python-telegram-bot~=11.0",
"twilio~=6.0",
"webexteamssdk~=1.1",
"mattermostwrapper~=2.0",
"rocketchat_API~=0.6.0",
"colorhash~=1.0",
"pika~=1.0.0",
Expand Down
2 changes: 0 additions & 2 deletions tests/core/test_channels.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,8 +209,6 @@ def test_mattermost_channel():
input_channel = MattermostInput(
# this is the url of the api for your mattermost instance
url="http://chat.example.com/api/v4",
# the name of your team for mattermost
team="community",
# the bot token of the bot account that will post messages
token="xxxxx",
# the password of your bot user that will post messages
Expand Down

0 comments on commit cedd4c3

Please sign in to comment.