コード例 #1
0
ファイル: bot_test.py プロジェクト: ryan-lane/omnibot
def test_team():
    _team = Team.get_team_by_name('testteam')
    _bot = Bot.get_bot_by_name(_team, 'echobot')
    assert _bot.name == 'echobot'
    assert _bot.bot_id == 'A12345678'
    assert _bot.team == _team
    assert _bot.oauth_user_token == '1234'
    assert _bot.oauth_bot_token == '1234'
    assert _bot.verification_token == '1234'

    _team = Team.get_team_by_id(team_id='TABCDEF12')
    _bot = Bot.get_bot_by_bot_id(_team, 'A98765432')
    assert _bot.name == 'echobot'
    assert _bot.bot_id == 'A98765432'
    assert _bot.team == _team
    assert _bot.oauth_user_token == '1234'
    assert _bot.oauth_bot_token == ''
    assert _bot.verification_token == '1234'

    _team = Team.get_team_by_name('testteam')
    _bot = Bot.get_bot_by_verification_token('5555')
    assert _bot.name == 'pingbot'
    assert _bot.bot_id == 'AABCDEF12'
    assert _bot.team == _team
    assert _bot.oauth_user_token == '5555'
    assert _bot.oauth_bot_token == '5555'
    assert _bot.verification_token == '5555'

    with pytest.raises(BotInitializationError):
        _bot = Bot.get_bot_by_name(_team, 'fakebot')

    with pytest.raises(BotInitializationError):
        _bot = Bot.get_bot_by_bot_id(_team, 'BADBOTID')
コード例 #2
0
ファイル: team_test.py プロジェクト: troybots/omnibot
def test_team():
    _team = Team.get_team_by_name('testteam')
    assert _team.name == 'testteam'
    assert _team.team_id == 'T12345678'

    _team = Team.get_team_by_id('TABCDEF12')
    assert _team.name == 'test2ndteam'
    assert _team.team_id == 'TABCDEF12'

    with pytest.raises(TeamInitializationError):
        _team = Team.get_team_by_name('faketeam')

    with pytest.raises(TeamInitializationError):
        _team = Team.get_team_by_id(team_id='BADTEAMID')
コード例 #3
0
def watch_channels():
    try:
        redis_client = omniredis.get_redis_client(decode_responses=False)
        last_run_key = "watch:channels:last_run_datetime"
        if not _is_allowed_to_run(redis_client, last_run_key):
            return

        statsd = stats.get_statsd_client()
        with redis_lock.Lock(redis_client,
                             'watch_channels',
                             expire=LOCK_EXPIRATION,
                             auto_renewal=True):
            with statsd.timer('watch.channels'):
                for team_name, bot_name in settings.PRIMARY_SLACK_BOT.items():
                    logger.info(
                        'Updating slack channel list.',
                        extra={
                            'team': team_name,
                            'bot': bot_name
                        },
                    )
                    team = Team.get_team_by_name(team_name)
                    bot = Bot.get_bot_by_name(team, bot_name)
                    slack.update_channels(bot)
            redis_client.set(last_run_key, datetime.now().isoformat())
    except Exception:
        logger.exception('Failed to update slack channel list.', exc_info=True)
    finally:
        return gevent.spawn_later(settings.WATCHER_SPAWN_WAIT_TIME_IN_SEC,
                                  watch_channels)
コード例 #4
0
ファイル: api.py プロジェクト: lyft/omnibot
def get_bot_ims(team_name, bot_name):
    """
    Returns list of IMs (DMs with a bot), for the provided
    `bot_name` and `team_name`.

    .. :quickref: Get a list of IMs for a bot within a team

    **Example request**:

    .. sourcecode:: http

       GET /api/v1/slack/get_ims/myteam/mybot

    **Example response**:

    .. sourcecode:: http

       HTTP/1.1 200 OK
       Content-Type: application/json

       {"ims":
            [
                {
                    "id": 'D1234567',
                    "created": 1518129625,
                    "is_im": true,
                    "is_org_shared": true,
                    "user": "******"
                    "is_user_deleted": false,
                    "priority": 0.01234567
                }
            ]
        }

    :param team_name: The team to search for this user, as configured in
                      omnibot.
    :type team_name: str
    :param bot_name: The bot to use for the request, as configured in omnibot.
    :type bot_name: str
    """
    try:
        team = Team.get_team_by_name(team_name)
    except TeamInitializationError:
        return jsonify({'error': 'provided team name was not found.'}), 404
    try:
        bot = Bot.get_bot_by_name(team, bot_name)
    except BotInitializationError:
        return jsonify({'error': 'provided bot name was not found.'}), 404
    raw_ims = slack.get_ims(bot)
    ims = []
    for im in raw_ims:
        # each im is a tuple where im[0] is the channel id and im[1] is the im object
        ims.append(json.loads(im[1]))
    return jsonify({'ims': ims})
コード例 #5
0
 def get_bot_by_verification_token(cls, verification_token):
     name = None
     _bot_data = {}
     for team_name, bots in settings.SLACK_BOT_TOKENS.items():
         for bot_name, bot_data in bots.items():
             if verification_token == bot_data['verification_token']:
                 name = bot_name
                 _bot_data = bot_data
                 break
     if not _bot_data:
         raise BotInitializationError('Invalid bot')
     team = Team.get_team_by_name(team_name)
     return cls(team, name, _bot_data)
コード例 #6
0
def test_interactive_block_component(mocker):
    _team = Team.get_team_by_name('testteam')
    _bot = Bot.get_bot_by_name(_team, 'echobot')
    component = {
        'type':
        'block_actions',
        'response_url':
        'https://hooks.slack.com/app/T999999/375455994771/iMW9hNKFI739hGOw9FCXMlf4',  # noqa:E501
        'actions': [{
            'block_id': 'echobot_action_test',
            'action_ts': '1561559117.130541'
        }]
    }
    event_trace = {
        'callback_id': 'echobot_action_test',
        'app_id': _bot.bot_id,
        'team_id': _bot.team.team_id,
        'bot_receiver': _bot.name,
        'component_type': 'message_action'
    }
    _component = InteractiveComponent(_bot, component, event_trace)
    assert _component.callback_id == 'echobot_action_test'
コード例 #7
0
ファイル: api.py プロジェクト: lyft/omnibot
 def decorated(*args, **kwargs):
     bot_name = request.view_args.get('bot_name')
     team_name = request.view_args.get('team_name')
     try:
         team = Team.get_team_by_name(team_name)
     except TeamInitializationError:
         logger.warning('Failed to validate bot',
                        extra={
                            'bot': bot_name,
                            'team': team_name,
                        })
         return abort(404)
     try:
         Bot.get_bot_by_name(team, bot_name)
     except BotInitializationError:
         logger.warning('Failed to validate bot',
                        extra={
                            'bot': bot_name,
                            'team': team_name,
                        })
         return abort(404)
     return f(*args, **kwargs)
コード例 #8
0
ファイル: api.py プロジェクト: lyft/omnibot
def get_team_id_by_name(team_name):
    """
    Get a team_id, from its `team_name`.

    .. :quickref: Team ID; Get team_id from team_name

    **Example request**:

    .. sourcecode:: http

       GET /api/v1/slack/get_team/myteam HTTP/1.1

    **Example response**:

    .. sourcecode:: http

       HTTP/1.1 200 OK
       Content-Type: application/json

       {"team_id": "T123456"}

    :param team_name: The team to search for this user, as configured in
                      omnibot.
    :type team_name: str
    :reqheader x-envoy-internal: Header that indicates whether or not this
                                 request is coming from an internal service
                                 or not. This is auto-set by envoy and doesn't
                                 need to be explicitly set.
    :resheader Content-Type: application/json
    :statuscode 200: success
    :statuscode 404: team is not configured
    """
    logger.debug('Getting team id', extra={'team': team_name})
    try:
        team = Team.get_team_by_name(team_name)
        return jsonify({'team_id': team.team_id})
    except TeamInitializationError:
        return jsonify({'error': 'provided team_name is not configured.'}), 404
コード例 #9
0
def help_callback(container):
    """
    Callback for omnibot help info.
    """
    payload = container.payload
    logger.debug('Help callback text: {}'.format(payload['text']))
    logger.debug('Help callback payload: {}'.format(
        json.dumps(payload, indent=2)))
    ret_action = {'action': 'chat.postMessage', 'kwargs': {'attachments': []}}
    ret = {'actions': [ret_action]}
    command_fields = []
    regex_fields = []
    team = Team.get_team_by_name(payload['team']['name'])
    bot = Bot.get_bot_by_name(team, payload['bot']['name'])
    for handler in bot.message_handlers:
        if handler['match_type'] == 'command':
            command_fields.append({
                'title': handler['match'],
                'value': handler.get('description', ''),
                'short': False
            })
        if handler['match_type'] == 'regex':
            regex_fields.append({
                'title': handler['match'],
                'value': handler.get('description', ''),
                'short': False
            })
    if command_fields:
        ret_action['kwargs']['attachments'].append({
            'title': 'Commands:',
            'fields': command_fields
        })
    if regex_fields:
        ret_action['kwargs']['attachments'].append({
            'title': 'Regex matches:',
            'fields': regex_fields
        })
    return ret
コード例 #10
0
def test_interactive_component(mocker):
    _team = Team.get_team_by_name('testteam')
    _bot = Bot.get_bot_by_name(_team, 'echobot')
    component = {
        'type': 'message_action',
        'callback_id': 'echobot_action_test',
        'action_ts': '1234567.12',
        'trigger_id':
        '376604117319.165116859648.515402022613c2893a80d6268c463e54',  # noqa:E501
        'response_url':
        'https://hooks.slack.com/app/T999999/375455994771/iMW9hNKFI739hGOw9FCXMlf4',  # noqa:E501
        'user': {
            'id': 'A12345678',
            'name': 'echobot'
        },
        'team': {
            'id': 'T999999',
            'domain': 'omnibot-test-domain'
        },
        'channel': {
            'id': 'C123456AB',
            'name': 'channel-channel'
        },
        'message': {
            'type': 'message',
            'user': '******',
            'text':
            '<@A12345678> echo I am <!here|here> in <#C123456AB|channel-channel>. See: <http://example.com> :simple_smile:',  # noqa:E501
            'ts': '1230000.00'
        }
    }
    event_trace = {
        'callback_id': 'echobot_action_test',
        'app_id': _bot.bot_id,
        'team_id': _bot.team.team_id,
        'bot_receiver': _bot.name,
        'component_type': 'message_action'
    }
    get_user_mock = mocker.patch('omnibot.services.slack.get_user')
    user_ret = {'A12345678': {}}
    get_user_mock.return_value = user_ret
    get_channel_mock = mocker.patch('omnibot.services.slack.get_channel')
    channel_ret = {'C123456AB': {}}
    get_channel_mock.return_value = channel_ret
    extract_users_mock = mocker.patch(
        'omnibot.services.slack.parser.extract_users')
    users_ret = {'<@A12345678>': 'echobot'}
    extract_users_mock.return_value = users_ret
    replace_users_mock = mocker.patch(
        'omnibot.services.slack.parser.replace_users')
    replace_users_mock.return_value = '@echobot echo I am <!here|here> in <#C123456AB|channel-channel>. See: <http://example.com> :simple_smile:'  # noqa:E501
    extract_channels_mock = mocker.patch(
        'omnibot.services.slack.parser.extract_channels')
    channels_ret = {'<#C123456AB|channel-channel>': 'channel-channel'}
    extract_channels_mock.return_value = channels_ret
    replace_channels_mock = mocker.patch(
        'omnibot.services.slack.parser.replace_channels')
    replace_channels_mock.return_value = '@echobot echo I am <!here|here> in #channel-channel. See: <http://example.com> :simple_smile:'  # noqa:E501
    extract_subteams_mock = mocker.patch(
        'omnibot.services.slack.parser.extract_subteams')
    extract_subteams_mock.return_value = {}
    extract_specials_mock = mocker.patch(
        'omnibot.services.slack.parser.extract_specials')
    special_ret = {'<!here|here>': '@here'}
    extract_specials_mock.return_value = special_ret
    replace_specials_mock = mocker.patch(
        'omnibot.services.slack.parser.replace_specials')
    replace_specials_mock.return_value = '@echobot echo I am @here in #channel-channel. See: <http://example.com> :simple_smile:'  # noqa:E501
    extract_emojis_mock = mocker.patch(
        'omnibot.services.slack.parser.extract_emojis')
    emoji_ret = {':simple-smile': 'simple_smile'}
    extract_emojis_mock.return_value = emoji_ret
    extract_emails_mock = mocker.patch(
        'omnibot.services.slack.parser.extract_emails')
    extract_emails_mock.return_value = {}
    replace_emails_mock = mocker.patch(
        'omnibot.services.slack.parser.replace_emails')
    replace_emails_mock.return_value = '@echobot echo I am @here in #channel-channel. See: <http://example.com> :simple_smile:'  # noqa:E501
    extract_urls_mock = mocker.patch(
        'omnibot.services.slack.parser.extract_urls')
    url_ret = {'<http://example.com>': 'http://example.com'}
    extract_urls_mock.return_value = url_ret
    replace_urls_mock = mocker.patch(
        'omnibot.services.slack.parser.replace_urls')
    replace_urls_mock.return_value = '@echobot echo I am @here in #channel-channel. See: http://example.com :simple_smile:'  # noqa:E501
    extract_mentions_mock = mocker.patch(
        'omnibot.services.slack.parser.extract_mentions')
    extract_mentions_mock.return_value = True
    extract_command_mock = mocker.patch(
        'omnibot.services.slack.parser.extract_command')
    extract_command_mock.return_value = 'echo I am @here in #channel-channel. See: http://example.com :simple_smile:'  # noqa:E501
    _component = InteractiveComponent(_bot, component, event_trace)
    assert _component.event_trace == event_trace
    assert _component.bot == _bot
    assert _component.component_type == 'message_action'
    assert _component.callback_id == 'echobot_action_test'
    assert _component.action_ts == '1234567.12'
    assert _component.trigger_id == '376604117319.165116859648.515402022613c2893a80d6268c463e54'  # noqa:E501
    assert _component.response_url == 'https://hooks.slack.com/app/T999999/375455994771/iMW9hNKFI739hGOw9FCXMlf4'  # noqa:E501
    assert _component.submission is None
    assert _component.channel['id'] == 'C123456AB'
    assert _component.parsed_channel == channel_ret
    assert _component.user == component['user']
    assert _component.team == {
        'name': _bot.team.name,
        'team_id': _bot.team.team_id
    }
    assert _component.message['parsed_user'] == user_ret
    assert _component.message['users'] == users_ret
    assert _component.message[
        'parsed_text'] == '@echobot echo I am @here in #channel-channel. See: http://example.com :simple_smile:'  # noqa:E501
    assert _component.message.get('bot_id') is None
    assert _component.message['channels'] == channels_ret
    assert _component.message['specials'] == special_ret
    assert _component.message['emails'] == {}
    assert _component.message['urls'] == url_ret
コード例 #11
0
ファイル: api.py プロジェクト: lyft/omnibot
def send_bot_im(team_name, bot_name, email):
    """
    Sends a message as a bot user to an IM (direct message) channel
    between a team member and a bot user,
    for the provided `team_name`, `bot_name`, and `email`.

    .. :quickref: Send an IM message between bot and user

    **Example request**:

    .. sourcecode:: http

       GET /api/v1/slack/send_im/myteam/mybot/[email protected]

    **Example response**:

    .. sourcecode:: http

       HTTP/1.1 200 OK
       Content-Type: application/json

       {
            "channel": "DC1234567",
            "message": {
                "bot_id": "BC1234567",
                "subtype": "bot_message",
                "text": "HI!",
                "ts": "1538593287.000100",
                "type": "message",
                "username": "******"
            },
            "ok": true,
            "ts": "1538593287.000100"
        }

    :param team_name: The team to search for the given bot,
                      as configured in omnibot.
    :type team_name: str
    :param bot_name: The bot sending the IM to the user,
                     as configured in omnibot.
    :type bot_name: str
    :param email: The email address of user to send message to
    :type email: str

    :resheader Content-Type: application/json
    :statuscode 200: success
    :statuscode 400: slack call returned a non-OK status
    :statuscode 404: team, bot, IM unable to be found,
                     or user deleted from slack team
    """
    data = request.json
    try:
        team = Team.get_team_by_name(team_name)
        bot = Bot.get_bot_by_name(team, bot_name)
    except TeamInitializationError:
        return jsonify({
            'error': 'provided team name was not found.',
            'team_name': team_name,
            'bot_name': bot_name,
            'email': email
        }), 404
    except BotInitializationError:
        return jsonify({
            'error': 'provided bot name was not found.',
            'team_name': team_name,
            'bot_name': bot_name,
            'email': email
        }), 404
    user = slack.get_user_by_email(bot, email)
    if not user:
        return jsonify({
            'error': 'unable to find slack user for given email.',
            'team_name': team_name,
            'bot_name': bot_name,
            'email': email
        }), 404
    im_id = slack.get_im_channel_id(bot, user['id'])
    if im_id is None:
        return jsonify({
            'error': 'unable to find IM channel.',
            'team_name': team_name,
            'bot_name': bot_name,
            'email': email
        }), 404
    data['kwargs']['channel'] = im_id
    ret = _perform_action(bot, data)
    if ret['ok']:
        return jsonify(ret), 200
    else:
        return jsonify(ret), 400
コード例 #12
0
ファイル: api.py プロジェクト: lyft/omnibot
def slack_action_v2(team_name, bot_name):
    """
    Perform an action against slack, as the provided `bot_name` in the
    provided `team_name`.

    .. :quickref: SlackAction; Perform a slack action in a specified team as
                  a specified bot.

    **Example request**:

    .. sourcecode:: http

       POST /api/v1/slack/action/myteam/mybot HTTP/1.1
       Content-Type: application/json

       {
         "action": "chat.postMessage",
         "kwargs": {
           "channel": "test-omnibot",
           "text": "@example see #general and use @here",
           "as_user": true,
           "omnibot_parse": {
             "text": ["channels", "users", "specials"]
           }
         }
       }

    :<json string action: slack api action to perform. example:
                          `chat.postMessage`
    :<json dict kwargs: keyword arguments you'd pass into the associated
                        slack api action. example:
                        `"text": "@example see #general and use @here"`
    :<json dict omnibot_parse: The keyword argument you'd like omnibot to
                               parse, with a list of things to parse. example:
                               {"text": ["channels", "users", "specials"]}

    **Example response**:

    .. sourcecode:: http

       HTTP/1.1 200 OK
       Content-Type: application/json

       {
         "channel": "C12345",
         "message": {
           "bot_id": "B456123",
           "subtype": "bot_message",
           "text": "<@UABC123|example> see <#C12345|general> and use <!here|here>",
           "ts": "1523557397.000335",
           "type": "message",
           "username": "******"
         },
         "ok": true,
         "ts": "1523557397.000335"
       }

    :param team_name: The team to perform this action against, as configured in
                      omnibot.
    :type team_name: str
    :param bot_name: The bot to use for the request, as configured in omnibot.
    :type bot_name: str
    :reqheader x-envoy-internal: Header that indicates whether or not this
                                 request is coming from an internal service
                                 or not. This is auto-set by envoy and doesn't
                                 need to be explicitly set.
    :resheader Content-Type: application/json
    :statuscode 200: success
    :statuscode 400: slack call returned a non-OK status
    """
    data = request.json
    try:
        team = Team.get_team_by_name(team_name)
    except TeamInitializationError:
        return jsonify({'error': 'provided team name was not found.'}), 404
    try:
        bot = Bot.get_bot_by_name(team, bot_name)
    except BotInitializationError:
        return jsonify({'error': 'provided bot name was not found.'}), 404
    ret = _perform_action(bot, data)
    if ret['ok']:
        return jsonify(ret), 200
    else:
        return jsonify(ret), 400
コード例 #13
0
ファイル: api.py プロジェクト: lyft/omnibot
def get_channel_by_name(team_name, bot_name, channel_name):
    """
    Returns a channel object from slack, for the provided `channel_name` in
    the `team_name` using the specified `bot_name`.

    .. :quickref: Channel; Get a channel from a team, via the channel_name

    **Example request**:

    .. sourcecode:: http

       GET /api/v1/slack/get_channel/myteam/mybot/general HTTP/1.1

    **Example response**:

    .. sourcecode:: http

       HTTP/1.1 200 OK
       Content-Type: application/json

       {
         "channel": {
           "id": "C4VQ6NUNN",
           "name": "general",
           "is_channel": true,
           "created": 1491515285,
           "creator": "U4WF56QGP",
           "is_archived": false,
           "is_general": true,
           "unlinked": 0,
           "name_normalized": "general",
           "is_shared": false,
           "is_org_shared": false,
           "is_member": false,
           "is_private": false,
           "is_mpim": false,
           "members": [
             "U4WF56QGP",
             "U6HQQ19EC",
             "U6J3LTKSQ",
             "U6J4EGP44",
             "U6JDF1JBU",
             "U6JEGTFDZ",
             "U6JERPMJ7",
             "U6JG691MJ",
             "U6JGEQ0J0",
             "U6SAVUK44",
             "U750C7B37",
             "U7DH0H802"
           ],
           "topic": {
             "value": "test123",
             "creator": "U6J3LTKSQ",
             "last_set": 1507156612
           },
           "purpose": {
             "value": "This channel is for team-wide communication.",
             "creator": "",
             "last_set": 0
           },
           "previous_names": [],
           "num_members": 9
         }
      }

    :param team_name: The team to search for this channel, as configured in
                      omnibot.
    :type team_name: str
    :param bot_name: The bot to use for the request, as configured in omnibot.
    :type bot_name: str
    :param channel_name: The name of the channel to get.
    :type channel_name: str
    :reqheader x-envoy-internal: Header that indicates whether or not this
                                 request is coming from an internal service
                                 or not. This is auto-set by envoy and doesn't
                                 need to be explicitly set.
    :resheader Content-Type: application/json
    :statuscode 200: success
    :statuscode 404: channel with specified channel_name could not be found
                     in the specified team using the specified bot.
    """
    logger.debug(
        'Getting channel for team={} bot={} channel={}.',
        extra={
            'team': team_name,
            'bot': bot_name,
            'channel': channel_name,
        },
    )
    try:
        team = Team.get_team_by_name(team_name)
    except TeamInitializationError:
        return jsonify({'error': 'provided team name was not found.'}), 404
    try:
        bot = Bot.get_bot_by_name(team, bot_name)
    except BotInitializationError:
        return jsonify({'error': 'provided bot name was not found.'}), 404
    channel = slack.get_channel_by_name(bot, channel_name)
    if channel is None:
        logger.debug(
            'Failed to get channel',
            extra=merge_logging_context(
                {'channel': channel_name},
                bot.logging_context,
            ),
        )
        return jsonify({'error': 'provided channel_name was not found.'}), 404
    return jsonify(channel)
コード例 #14
0
ファイル: api.py プロジェクト: lyft/omnibot
def get_user_v2(team_name, bot_name, email):
    """
    Returns basic user information, for the provided `email` in the `team_name`
    using the specified `bot_name`.

    .. :quickref: User; Get a user from a team, via their email

    **Example request**:

    .. sourcecode:: http

       GET /api/v1/slack/get_user/myteam/mybot/[email protected] HTTP/1.1

    **Example response**:

    .. sourcecode:: http

       HTTP/1.1 200 OK
       Content-Type: application/json

       {"email": "*****@*****.**", "name": "Test User", "team_id": "T123456",
        "user_id": "U123ABC"}

    :param team_name: The team to search for this user, as configured in
                      omnibot.
    :type team_name: str
    :param bot_name: The bot to use for the request, as configured in omnibot.
    :type bot_name: str
    :param email: The email address of the user to get.
    :type email: str
    :reqheader x-envoy-internal: Header that indicates whether or not this
                                 request is coming from an internal service
                                 or not. This is auto-set by envoy and doesn't
                                 need to be explicitly set.
    :resheader Content-Type: application/json
    :statuscode 200: success
    :statuscode 404: user with specified email could not be found using the
                     specified bot.
    """
    logger.debug('Getting user team={} bot={} email={}.',
                 extra={
                     'team': team_name,
                     'bot': bot_name,
                     'email': email,
                 })
    try:
        team = Team.get_team_by_name(team_name)
    except TeamInitializationError:
        return jsonify({'error': 'provided team name was not found.'}), 404
    try:
        bot = Bot.get_bot_by_name(team, bot_name)
    except BotInitializationError:
        return jsonify({'error': 'provided bot name was not found.'}), 404
    user = slack.get_user_by_email(bot, email)
    if not user:
        return jsonify({'error': 'user not found'}, ), 404
    name = slack.get_name_from_user(user)
    return jsonify({
        'user': {
            'email': email,
            'name': name,
            'team_id': team.team_id,
            'user_id': user['id']
        }
    })
コード例 #15
0
def test_message(mocker):
    _team = Team.get_team_by_name('testteam')
    _bot = Bot.get_bot_by_name(_team, 'echobot')
    event = {
        'ts': '1234567.12',
        'thread_ts': None,
        'user': '******',
        'text':
        '<@A12345678> echo I am <!here|here> in <#C123456AB|channel-channel>. See: <http://example.com> :simple_smile:',  # noqa:E501
        'channel': 'C123456AB',
    }
    event_trace = {
        'event_ts': '1234567.12',
        'event_type': 'message',
        'app_id': _bot.bot_id,
        'team_id': _bot.team.team_id,
        'bot_receiver': _bot.name
    }
    get_user_mock = mocker.patch('omnibot.services.slack.get_user')
    user_ret = {'A12345678': {}}
    get_user_mock.return_value = user_ret
    get_channel_mock = mocker.patch('omnibot.services.slack.get_channel')
    channel_ret = {'C123456AB': {}}
    get_channel_mock.return_value = channel_ret
    extract_users_mock = mocker.patch(
        'omnibot.services.slack.parser.extract_users')
    user_ret = {'<@A12345678>': 'echobot'}
    extract_users_mock.return_value = user_ret
    replace_users_mock = mocker.patch(
        'omnibot.services.slack.parser.replace_users')
    replace_users_mock.return_value = '@echobot echo I am <!here|here> in <#C123456AB|channel-channel>. See: <http://example.com> :simple_smile:'  # noqa:E501
    extract_channels_mock = mocker.patch(
        'omnibot.services.slack.parser.extract_channels')
    channels_ret = {'<#C123456AB|channel-channel>': 'channel-channel'}
    extract_channels_mock.return_value = channels_ret
    replace_channels_mock = mocker.patch(
        'omnibot.services.slack.parser.replace_channels')
    replace_channels_mock.return_value = '@echobot echo I am <!here|here> in #channel-channel. See: <http://example.com> :simple_smile:'  # noqa:E501
    extract_subteams_mock = mocker.patch(
        'omnibot.services.slack.parser.extract_subteams')
    extract_subteams_mock.return_value = {}
    extract_specials_mock = mocker.patch(
        'omnibot.services.slack.parser.extract_specials')
    special_ret = {'<!here|here>': '@here'}
    extract_specials_mock.return_value = special_ret
    replace_specials_mock = mocker.patch(
        'omnibot.services.slack.parser.replace_specials')
    replace_specials_mock.return_value = '@echobot echo I am @here in #channel-channel. See: <http://example.com> :simple_smile:'  # noqa:E501
    extract_emojis_mock = mocker.patch(
        'omnibot.services.slack.parser.extract_emojis')
    emoji_ret = {':simple-smile': 'simple_smile'}
    extract_emojis_mock.return_value = emoji_ret
    extract_emails_mock = mocker.patch(
        'omnibot.services.slack.parser.extract_emails')
    extract_emails_mock.return_value = {}
    replace_emails_mock = mocker.patch(
        'omnibot.services.slack.parser.replace_emails')
    replace_emails_mock.return_value = '@echobot echo I am @here in #channel-channel. See: <http://example.com> :simple_smile:'  # noqa:E501
    extract_urls_mock = mocker.patch(
        'omnibot.services.slack.parser.extract_urls')
    url_ret = {'<http://example.com>': 'http://example.com'}
    extract_urls_mock.return_value = url_ret
    replace_urls_mock = mocker.patch(
        'omnibot.services.slack.parser.replace_urls')
    replace_urls_mock.return_value = '@echobot echo I am @here in #channel-channel. See: http://example.com :simple_smile:'  # noqa:E501
    extract_mentions_mock = mocker.patch(
        'omnibot.services.slack.parser.extract_mentions')
    extract_mentions_mock.return_value = True
    extract_command_mock = mocker.patch(
        'omnibot.services.slack.parser.extract_command')
    extract_command_mock.return_value = 'echo I am @here in #channel-channel. See: http://example.com :simple_smile:'  # noqa:E501
    _message = Message(_bot, event, event_trace)
    assert _message.event == event
    assert _message.event_trace == event_trace
    assert _message.bot == _bot
    assert _message.subtype is None
    assert _message.text == event['text']
    assert _message.parsed_text == '@echobot echo I am @here in #channel-channel. See: http://example.com :simple_smile:'  # noqa:E501
    assert _message.command_text == 'echo I am @here in #channel-channel. See: http://example.com :simple_smile:'  # noqa:E501
    assert _message.directed is True
    assert _message.mentioned is True
    assert _message.channel_id == 'C123456AB'
    assert _message.channel == channel_ret
    assert _message.user == event['user']
    assert _message.ts == event['ts']
    assert _message.thread_ts == event['thread_ts']
    assert _message.team == {
        'name': _bot.team.name,
        'team_id': _bot.team.team_id
    }
    assert _message.bot_id is None
    assert _message.channels == channels_ret
    assert _message.users == user_ret
    assert _message.specials == special_ret
    assert _message.emails == {}
    assert _message.urls == url_ret
    assert _message.match_type is None
    assert _message.match is None
    assert _message.event_trace == event_trace
    _message.set_match('command', 'echo')
    assert _message.match_type == 'command'
    assert _message.match == 'echo'
    assert _message.payload['command'] == 'echo'
    assert _message.payload[
        'args'] == 'I am @here in #channel-channel. See: http://example.com :simple_smile:'  # noqa:E501
    _message.set_match('regex', None)
    assert _message.match_type == 'regex'

    event_copy = copy.deepcopy(event)
    event_copy['bot_id'] = 'A12345678'
    with pytest.raises(MessageUnsupportedError):
        _message = Message(_bot, event_copy, event_trace)

    event_copy = copy.deepcopy(event)
    event_copy['thread_ts'] = '1234568.00'
    with pytest.raises(MessageUnsupportedError):
        _message = Message(_bot, event_copy, event_trace)

    event_copy = copy.deepcopy(event)
    event_copy['subtype'] = 'some_subtype'
    with pytest.raises(MessageUnsupportedError):
        _message = Message(_bot, event_copy, event_trace)