SlackAsk

Sends a message (question) to either user (in a direct message) or to a channel. The message includes predefined reply options. The response can also close a task (might be conditional) in a playbook.

python · Slack

Details

IDSlackAsk
Languagepython
From Version5.0.0
Docker Imagedemisto/python3:3.12.13.10116658
Tagsslack

README

Sends a message (question) to either user (in a direct message) or to a channel. The message includes predefined reply options. The response can also close a task (might be conditional) in a playbook.

Use Case

This automation allows you to ask users in Slack(including external to Cortex XSOAR) questions, have them respond and reflect the answer back to Cortex XSOAR.

Prerequisites

Requires an instance of the Slack v2 integration.

Inputs

Argument Name Description Required
user The Slack user to which to send the message. Can be either an email address or a Slack user name. Optional
channel The Slack channel to which to send the message. Optional
message The message to send to the user or channel. Required
option1 The first reply option. The default is "Yes" with a green button. To change the color of the button, add the pound sign (#) followed by the name of the new color (green, red, or black). The default color is "green". For example, "Yes#green". Optional
option2 The second reply option. The default is "No" with a red button. To change the button color, add the pound sign (#) followed by the name of the new color (green, red, or black). The default color is "red". For example, "No#red". Optional
task The task number or task tag to close with the reply. If empty, then no playbook tasks will be closed. We recommend using a task tag, as task number might change between playbook (or sub-playbook) executions. Optional
replyEntriesTag Tag to add to email reply entries. Optional
responseType How the user should respond to the question. Default is "buttons". Optional
additionalOptions A comma-separated list of additional options in the format of "option#color", for example, "maybe#red". The default color is "black". Optional
reply The reply to send to the user. Use the templates {user} and {response} to incorporate these in the reply (i.e. "Thank you {user}. You have answered {response}."). Default is "Thank you for your response.". Optional
lifetime Time until the question expires. For example - 1 day. When it expires, a default response is sent. Optional
defaultResponse Default response in case the question expires. Default is "NoResponse". Optional

Guide

The automation is most useful in a playbook to determine the outcome of a conditional task - which will be one of the provided options. It uses a mechanism that allows external users to respond in Cortex XSOAR(per investigation) with entitlement strings embedded within the message contents.
The automation can utilize the interactive capabilities of Slack to send a form with buttons - this requires the external endpoint for interactive responses to be available for connection (See the Slack v2 intergation documentation for more information). You can also utilize threads instead, simply by specifying the responseType argument.

</span>

import datetime
import json

import dateparser
import demistomock as demisto
import SlackAsk
from CommonServerPython import entryTypes

BLOCKS = [
    {"type": "section", "text": {"type": "mrkdwn", "text": "wat up"}},
    {
        "type": "actions",
        "elements": [
            {
                "type": "button",
                "text": {"type": "plain_text", "emoji": True, "text": "yes"},
                "value": '{"entitlement": "4404dae8-2d45-46bd-85fa-64779c12abe8@22", "reply": "Thank you brother."}',
                "style": "danger",
            },
            {
                "type": "button",
                "text": {"type": "plain_text", "emoji": True, "text": "no"},
                "value": '{"entitlement": "4404dae8-2d45-46bd-85fa-64779c12abe8@22", "reply": "Thank you brother."}',
                "style": "danger",
            },
        ],
    },
]

BLOCKS_ADDITIONAL = [
    {"type": "section", "text": {"type": "mrkdwn", "text": "wat up"}},
    {
        "type": "actions",
        "elements": [
            {
                "type": "button",
                "text": {"type": "plain_text", "emoji": True, "text": "yes"},
                "value": '{"entitlement": "4404dae8-2d45-46bd-85fa-64779c12abe8@22", "reply": "Thank you brother."}',
                "style": "danger",
            },
            {
                "type": "button",
                "text": {"type": "plain_text", "emoji": True, "text": "no"},
                "value": '{"entitlement": "4404dae8-2d45-46bd-85fa-64779c12abe8@22", "reply": "Thank you brother."}',
                "style": "danger",
            },
            {
                "type": "button",
                "text": {"type": "plain_text", "emoji": True, "text": "maybe"},
                "value": '{"entitlement": "4404dae8-2d45-46bd-85fa-64779c12abe8@22", "reply": "Thank you brother."}',
            },
        ],
    },
]


def execute_command(command, args):
    if command == "addEntitlement":
        return [{"Type": entryTypes["note"], "Contents": "4404dae8-2d45-46bd-85fa-64779c12abe8"}]

    return []


def test_slack_ask_user(mocker):
    # Set
    mocker.patch.object(demisto, "executeCommand", side_effect=execute_command)
    mocker.patch.object(demisto, "investigation", return_value={"id": "22"})
    mocker.patch.object(
        demisto,
        "args",
        return_value={
            "user": "alexios",
            "message": "wat up",
            "option1": "yes#red",
            "option2": "no#red",
            "reply": "Thank you brother.",
            "lifetime": "24 hours",
            "defaultResponse": "NoResponse",
            "using-brand": "SlackV2",
        },
    )
    mocker.patch.object(demisto, "results")
    mocker.patch.object(dateparser, "parse", return_value=datetime.datetime(2019, 9, 26, 18, 38, 25))

    # Arrange
    SlackAsk.main()
    call_args = demisto.executeCommand.call_args[0]

    # Assert
    assert call_args[1] == {
        "ignoreAddURL": "true",
        "using-brand": "SlackV2",
        "blocks": json.dumps(
            {
                "blocks": json.dumps(BLOCKS),
                "entitlement": "4404dae8-2d45-46bd-85fa-64779c12abe8@22",
                "reply": "Thank you brother.",
                "expiry": "2019-09-26 18:38:25",
                "default_response": "NoResponse",
            }
        ),
        "message": "wat up",
        "to": "alexios",
    }


def test_slack_ask_user_additional(mocker):
    # Set
    mocker.patch.object(demisto, "executeCommand", side_effect=execute_command)
    mocker.patch.object(demisto, "investigation", return_value={"id": "22"})
    mocker.patch.object(
        demisto,
        "args",
        return_value={
            "user": "alexios",
            "message": "wat up",
            "option1": "yes#red",
            "option2": "no#red",
            "additionalOptions": "maybe",
            "reply": "Thank you brother.",
            "lifetime": "24 hours",
            "defaultResponse": "NoResponse",
            "using-brand": "SlackV2",
        },
    )
    mocker.patch.object(demisto, "results")
    mocker.patch.object(dateparser, "parse", return_value=datetime.datetime(2019, 9, 26, 18, 38, 25))

    # Arrange
    SlackAsk.main()
    call_args = demisto.executeCommand.call_args[0]

    # Assert
    assert call_args[1] == {
        "ignoreAddURL": "true",
        "using-brand": "SlackV2",
        "blocks": json.dumps(
            {
                "blocks": json.dumps(BLOCKS_ADDITIONAL),
                "entitlement": "4404dae8-2d45-46bd-85fa-64779c12abe8@22",
                "reply": "Thank you brother.",
                "expiry": "2019-09-26 18:38:25",
                "default_response": "NoResponse",
            }
        ),
        "message": "wat up",
        "to": "alexios",
    }


def test_slack_ask_channel(mocker):
    # Set
    mocker.patch.object(demisto, "executeCommand", side_effect=execute_command)
    mocker.patch.object(demisto, "investigation", return_value={"id": "22"})
    mocker.patch.object(
        demisto,
        "args",
        return_value={
            "channel": "general",
            "message": "wat up",
            "option1": "yes#red",
            "option2": "no#red",
            "reply": "Thank you brother.",
            "lifetime": "24 hours",
            "defaultResponse": "NoResponse",
            "using-brand": "SlackV2",
        },
    )
    mocker.patch.object(demisto, "results")
    mocker.patch.object(dateparser, "parse", return_value=datetime.datetime(2019, 9, 26, 18, 38, 25))

    # Arrange
    SlackAsk.main()
    call_args = demisto.executeCommand.call_args[0]

    # Assert
    assert call_args[1] == {
        "ignoreAddURL": "true",
        "using-brand": "SlackV2",
        "blocks": json.dumps(
            {
                "blocks": json.dumps(BLOCKS),
                "entitlement": "4404dae8-2d45-46bd-85fa-64779c12abe8@22",
                "reply": "Thank you brother.",
                "expiry": "2019-09-26 18:38:25",
                "default_response": "NoResponse",
            }
        ),
        "message": "wat up",
        "channel": "general",
    }


def test_slack_ask_user_threads(mocker):
    # Set
    mocker.patch.object(demisto, "executeCommand", side_effect=execute_command)
    mocker.patch.object(demisto, "investigation", return_value={"id": "22"})
    mocker.patch.object(
        demisto,
        "args",
        return_value={
            "user": "alexios",
            "message": "wat up",
            "responseType": "thread",
            "option1": "yes#red",
            "option2": "no#red",
            "reply": "Thank you brother.",
            "lifetime": "24 hours",
            "defaultResponse": "NoResponse",
            "using-brand": "SlackV2",
        },
    )
    mocker.patch.object(demisto, "results")
    mocker.patch.object(dateparser, "parse", return_value=datetime.datetime(2019, 9, 26, 18, 38, 25))

    # Arrange
    SlackAsk.main()
    call_args = demisto.executeCommand.call_args[0]

    # Assert
    assert call_args[1] == {
        "message": json.dumps(
            {
                "message": "wat up - Please reply to this thread with `yes` or `no`.",
                "entitlement": "4404dae8-2d45-46bd-85fa-64779c12abe8@22",
                "reply": "Thank you brother.",
                "expiry": "2019-09-26 18:38:25",
                "default_response": "NoResponse",
            }
        ),
        "ignoreAddURL": "true",
        "using-brand": "SlackV2",
        "to": "alexios",
    }


def test_slack_ask_user_threads_additional(mocker):
    # Set
    mocker.patch.object(demisto, "executeCommand", side_effect=execute_command)
    mocker.patch.object(demisto, "investigation", return_value={"id": "22"})
    mocker.patch.object(
        demisto,
        "args",
        return_value={
            "user": "alexios",
            "message": "wat up",
            "option1": "yes#red",
            "option2": "no#red",
            "additionalOptions": "maybe",
            "responseType": "thread",
            "reply": "Thank you brother.",
            "lifetime": "24 hours",
            "defaultResponse": "NoResponse",
            "using-brand": "SlackV2",
        },
    )
    mocker.patch.object(demisto, "results")
    mocker.patch.object(dateparser, "parse", return_value=datetime.datetime(2019, 9, 26, 18, 38, 25))

    # Arrange
    SlackAsk.main()
    call_args = demisto.executeCommand.call_args[0]

    # Assert
    assert call_args[1] == {
        "message": json.dumps(
            {
                "message": "wat up - Please reply to this thread with `yes` or `no` or `maybe`.",
                "entitlement": "4404dae8-2d45-46bd-85fa-64779c12abe8@22",
                "reply": "Thank you brother.",
                "expiry": "2019-09-26 18:38:25",
                "default_response": "NoResponse",
            }
        ),
        "ignoreAddURL": "true",
        "using-brand": "SlackV2",
        "to": "alexios",
    }


def test_slack_ask_channel_threads(mocker):
    # Set
    mocker.patch.object(demisto, "executeCommand", side_effect=execute_command)
    mocker.patch.object(demisto, "investigation", return_value={"id": "22"})
    mocker.patch.object(
        demisto,
        "args",
        return_value={
            "channel": "general",
            "message": "wat up",
            "responseType": "thread",
            "option1": "yes#red",
            "option2": "no#red",
            "reply": "Thank you brother.",
            "lifetime": "24 hours",
            "defaultResponse": "NoResponse",
            "using-brand": "SlackV2",
        },
    )
    mocker.patch.object(demisto, "results")
    mocker.patch.object(dateparser, "parse", return_value=datetime.datetime(2019, 9, 26, 18, 38, 25))

    # Arrange
    SlackAsk.main()
    call_args = demisto.executeCommand.call_args[0]

    # Assert
    assert call_args[1] == {
        "message": json.dumps(
            {
                "message": "wat up - Please reply to this thread with `yes` or `no`.",
                "entitlement": "4404dae8-2d45-46bd-85fa-64779c12abe8@22",
                "reply": "Thank you brother.",
                "expiry": "2019-09-26 18:38:25",
                "default_response": "NoResponse",
            }
        ),
        "ignoreAddURL": "true",
        "using-brand": "SlackV2",
        "channel": "general",
    }