def test_add_section():
    fake_slack = Slack(slack_token="fake")
    fake_slack.add_section_block(text="Fake section")
    assert isinstance(fake_slack.blocks, list)
    assert "type" in fake_slack.blocks[0].keys()
    assert "text" in fake_slack.blocks[0].keys()
    assert isinstance(fake_slack.blocks[0].get("text"), dict)
Beispiel #2
0
    def __init__(self, payload, config):
        self.payload = payload

        try:
            self.params = self.payload["text"][0].split()
        except KeyError:
            log.info("No parameters received. Defaulting to help action")
            self.params = ["help"]

        self.config = config
        self.bot_access_token = config["bot_access_token"]
        self.slack = Slack(slack_token=config["bot_access_token"])
        self.response_url = self.payload["response_url"][0]
def test_remind_user_without_lock():
    when(requests).get(url=f"{fake_config['backend_url']}/users",
                       **kwargs).thenReturn(
                           mock({
                               "status_code": 200,
                               "text": json.dumps(dict(testuser="******"))
                           }))

    when(requests).get(
        url=f"{fake_config['backend_url']}/users/testuser/locks",
        **kwargs,
    ).thenReturn(mock({
        "status_code": 200,
        "text": json.dumps([])
    }))

    when(requests).post(url="https://slack.com/api/chat.postMessage",
                        **kwargs).thenReturn(mock({"status_code": 200}))

    remind_users(Slack(fake_config["bot_access_token"]),
                 fake_config["backend_url"])

    verify(requests,
           times=1).post(url="https://slack.com/api/chat.postMessage",
                         **kwargs)

    unstub()
def remind_users(slack: Slack,
                 backend_url: str,
                 only_for_user_ids: Optional[List[str]] = None) -> List[str]:
    api_url = f"{backend_url}/users"
    res = requests.get(url=api_url)
    if res.status_code != 200:
        logger.error(f"Failed to load users: {res.text}")
        return

    month = last_month()

    reminded_users = []

    for user_id, name in json.loads(res.text).items():
        if only_for_user_ids and user_id not in only_for_user_ids:
            continue
        lock_url = f"{backend_url}/users/{user_id}/locks"
        lock_res = requests.get(url=lock_url)
        if res.status_code != 200:
            logger.error(f"Failed to load user locks {user_id}: {res.text}")
            continue

        locked = False
        for lock in json.loads(lock_res.text):
            if lock.get("event_date") == month:
                locked = True

        if not locked:
            slack_res = slack.post_message(
                channel=user_id,
                message=f":unlock: You have not locked {month}",
                as_user=False,
            )
            if slack_res.status_code != 200:
                logger.error(f"Failed to notify slack: {slack_res.text}")
                continue

            reminded_users.append(user_id)

    return reminded_users
def test_remind_user_with_lock():
    when(requests).get(url=f"{fake_config['backend_url']}/users",
                       **kwargs).thenReturn(
                           mock({
                               "status_code": 200,
                               "text": json.dumps(dict(testuser="******"))
                           }))

    lock_evt = dict(event_date=last_month(), user_id="testuser")
    when(requests).get(
        url=f"{fake_config['backend_url']}/users/testuser/locks",
        **kwargs,
    ).thenReturn(mock({
        "status_code": 200,
        "text": json.dumps([lock_evt])
    }))

    with expect(requests, times=0).post(...):
        remind_users(Slack(fake_config["bot_access_token"]),
                     fake_config["backend_url"])

    unstub()
Beispiel #6
0
def check_user_locks(event):
    if config["enable_lock_reminder"]:
        remind_users(
            slack=Slack(slack_token=config["bot_access_token"]),
            backend_url=config["backend_url"],
        )
Beispiel #7
0
class Action:
    def __init__(self, payload, config):
        self.payload = payload

        try:
            self.params = self.payload["text"][0].split()
        except KeyError:
            log.info("No parameters received. Defaulting to help action")
            self.params = ["help"]

        self.config = config
        self.bot_access_token = config["bot_access_token"]
        self.slack = Slack(slack_token=config["bot_access_token"])
        self.response_url = self.payload["response_url"][0]

    def perform_action(self):
        """
        Perform action.

        Supported actions are:
        add - Add new post in timereport
        edit - Not implemented yet
        delete - Delete post in timereport
        list - List posts in timereport
        lock - Not implemented yet
        help - Provide this helpful output
        """

        self.action = self.params[0]
        log.debug(f"Action is: {self.action}")
        self.user_id = self.payload["user_id"][0]

        if self.action == "add":
            return self._add_action()

        if self.action == "edit":
            return self._edit_action()

        if self.action == "delete":
            return self._delete_action()

        if self.action == "list":
            return self._list_action()

        if self.action == "lock":
            return self._lock_action()

        if self.action == "help":
            return self._help_action()

        return self._unsupported_action()

    def _unsupported_action(self):
        log.info(f"Action: {self.action} is not supported")
        return self.send_response(message=f"Unsupported action: {self.action}")

    def _add_action(self):
        events = factory(self.payload)
        if not events:
            return self.send_response(
                message="Wrong arguments for add command")

        log.info(f"Events is: {events}")
        user_name = events[0].get("user_name")[0]
        reason = events[0].get("reason")

        if not reason in self.config.get('valid_reasons'):
            message = f"Reason {reason} is not valid"
            log.debug(message)
            self.send_response(message=message)
            return ""

        self.date_start = events[0].get("event_date")
        self.date_end = events[-1].get("event_date")
        hours = events[0].get("hours")

        if self.check_lock_state():
            self.send_response(message="One or more of the events are locked")
            return ""

        self.send_attachment(attachment=submit_message_menu(
            user_name, reason, self.date_start, self.date_end, hours))
        return ""

    def _list_action(self):
        """
        List timereport for user.
        If no arguments supplied it will default to all.

        Supported arguments:
        "today" - List the event for the todays date
        "date" - The date as a string.
        """
        arguments = self.params[1:]

        log.debug(f"Got arguments: {arguments}")
        try:
            if arguments[0] == "today":
                date_str = datetime.now().strftime("%Y-%m-%d")
            else:
                date_str = arguments[0]
        except IndexError:
            month = datetime.now().strftime("%Y-%m")
            # A hack to set the date_str to the current month
            date_str = f"{month}-01:{month}-31"
            arguments = self.params[1:]
        except Exception as error:
            log.debug(f"got unexpected exception: {error}", exc_info=True)
            self.send_response(
                message=f"Got unexpected error with arguments: {arguments}")
            return ""

        log.debug(f"The date string set to: {date_str}")
        list_data = self._get_events(date_str=date_str)

        if not list_data or list_data == '[]':
            log.debug(f"List returned nothing. Date string was: {date_str}")
            self.send_response(
                message=
                f"Sorry, nothing to list with supplied argument {arguments}")
            return ""

        self.send_response(message=f"{list_data}")
        return ""

    def _delete_action(self):
        date = self.params[1]
        self.send_attachment(attachment=delete_message_menu(
            self.payload.get("user_name")[0], date))
        return ""

    def _edit_action(self):
        return self.send_response(message="Edit not implemented yet")

    def send_response(self, message):
        """
        Send a response to slack

        :message: The Message to send
        """
        log.debug("Sending message to slack")

        if not self.slack.is_conversation_open:
            log.debug("Need to open slack conversation")
            self.slack.open_conversation(slack_user_id=self.user_id)

        if not self.slack.slack_dm_channel:
            log.debug("Need to find slack direct message channel ID")
            self.slack.open_conversation(slack_user_id=self.user_id)

        self.slack.send_message(message=message)

        return ""

    def send_attachment(self, attachment):
        """
        Send an message to slack using attachment
        :attachment: The attachment (slack specific attachment) to send
        """

        slack_client_response = slack_client_responder(
            token=self.bot_access_token,
            user_id=self.slack.slack_dm_channel,
            attachment=attachment,
        )

        if slack_client_response.status_code != 200:
            log.error(
                f"""Failed to send response to slack. Status code was: {slack_client_response.status_code}.
                The response from slack was: {slack_client_response.text}""")
            return "Slack response to user failed"
        else:
            log.debug(
                f"Slack client response was: {slack_client_response.text}")
        return slack_client_response

    def _help_action(self):
        return self.send_response(message=f"{self.perform_action.__doc__}")

    def check_lock_state(self):
        """
        Go through events and check if locked

        Return true if any locked events found
        """

        for event in json.loads(
                self._get_events(
                    date_str=f"{self.date_start}:{self.date_end}")):
            if event.get("lock"):
                return True

        return False

    def _get_events(self, date_str):
        return get_list_data(
            f"{self.config['backend_url']}",
            self.user_id,
            date_str=date_str,
        )

    def _lock_action(self):
        """
        /timereport-dev lock 2019-08
        """
        event = create_lock(user_id=self.user_id, event_date=self.params[1])
        log.debug(f"lock event: {event}")
        response = lock_event(url=self.config['backend_url'],
                              event=json.dumps(event))
        log.debug(f"response was: {response.text}")
        if response.status_code == 200:
            self.send_response(message=f"Lock successful! :lock: :+1:")
            return ""
        else:
            self.send_response(message=f"Lock failed! :cry:")
            return ""
def test_slack_class():
    test = Slack(slack_token="fake_token")
    assert test.slack_token
    assert test.client
Beispiel #9
0
from chalicelib.lib.helpers import parse_config
from chalicelib.action import Action

app = Chalice(app_name='timereport')
app.debug = True

logger = logging.getLogger()

dir_path = os.path.dirname(os.path.realpath(__file__))
config = parse_config(f'{dir_path}/chalicelib/config.yaml')
config['backend_url'] = os.getenv('backend_url')
config['bot_access_token'] = os.getenv('bot_access_token')
config['signing_secret'] = os.getenv('signing_secret')
logger.setLevel(config['log_level'])
slack = Slack(slack_token=config["bot_access_token"])


@app.route('/interactive',
           methods=['POST'],
           content_types=['application/x-www-form-urlencoded'])
def interactive():
    req = app.current_request.raw_body.decode()
    payload = slack_payload_extractor(req)
    selection = payload.get('actions')[0].get('value')
    user_id = payload['user']['id']

    if not slack.is_conversation_open:
        logger.debug("Need to open slack conversation")
        slack.open_conversation(slack_user_id=user_id)
def test_add_divider():
    fake_slack = Slack(slack_token="fake")
    fake_slack.add_divider_block()
    assert isinstance(fake_slack.blocks, list)
    assert "type" in fake_slack.blocks[0].keys()
    assert "block_id" in fake_slack.blocks[0].keys()
import os
from mockito import when, mock, unstub
from .test_data import fake_request_body
import requests
from chalicelib.lib.slack import (
    slack_payload_extractor,
    verify_token,
    submit_message_menu,
    delete_message_menu,
    slack_client_responder,
    slack_responder,
    Slack,
)

fake_slack = Slack(slack_token="fake")


def test_slack_payload_extractor_command():
    fake_data = slack_payload_extractor(
        "command=bar&text=fake+text&user_id=fake_user_id&response_url=http://example.com&user_name=mrFaky"
    )
    assert isinstance(fake_data, dict)
    assert fake_data.get("command") == "bar"
    assert fake_data.get("text") == "fake text"
    assert fake_data.get("user_id")
    assert fake_data.get("response_url")


def test_slack_payload_extractor_payload():
    fake_data = slack_payload_extractor(
        "payload=%7B%0D%0A+++%22fake%22%3A+%22fake+data%22%0D%0A%7D")