Beispiel #1
0
def outgoing(request) -> MegatronResponse:
    user_id = request.data['user']
    message = request.data['message']
    try:
        message['attachments'] = json.loads(message['attachments'])
    except TypeError:
        pass
    try:
        megatron_channel = MegatronChannel.objects.get(
            platform_user_id=user_id)
    except MegatronChannel.DoesNotExist:
        return MegatronResponse({'ok': True, 'track': False}, 200)
    if megatron_channel.is_archived:
        return MegatronResponse({'ok': True, 'track': False}, 200)

    megatron_integration = megatron_channel.megatron_integration
    interpreter = IntegrationService(megatron_integration).get_interpreter()
    customer_workspace_id = megatron_channel.workspace.platform_id
    response = interpreter.outgoing(message, megatron_channel)
    if response.get('ok'):
        if response.get('watched_channel'):
            front_integration.outgoing_message.delay(user_id,
                                                     customer_workspace_id,
                                                     message)
            megatron_msg = MegatronMessage(integration_msg_id=response['ts'],
                                           customer_msg_id=request.data['ts'],
                                           megatron_channel=megatron_channel)
            megatron_msg.save()

        return MegatronResponse({'ok': True, 'track': True}, 200)
    return MegatronResponse({
        'error': response.get('error'),
        'track': False
    }, 500)
Beispiel #2
0
def open_channel(
    megatron_user_id: int, serialized_request_data: dict, arguments: dict
) -> dict:
    request_data = RequestData(**serialized_request_data)
    megatron_user = MegatronUser.objects.get(id=megatron_user_id)
    platform_user_id = arguments["targeted_platform_id"]
    integration = megatron_user.megatronintegration_set.first()
    connection = IntegrationService(integration).get_connection(as_user=False)

    # Ensure platform user exists
    if not PlatformUser.objects.filter(platform_id=platform_user_id).exists():
        user_info = connection._get_user_info(platform_user_id)
        try:
            workspace = CustomerWorkspace.objects.get(platform_id=user_info["user"]["team_id"])
        except (CustomerWorkspace.DoesNotExist, KeyError):
            return {"ok": False, "error": "Customer Workspace not found"}
        WorkspaceService(workspace).get_or_create_user_by_id(platform_user_id)

    new_msg = formatting.user_titled(platform_user_id, "Connecting...")
    response = connection.respond_to_url(request_data.response_url, new_msg)
    if not response.get("ok"):
        return response
    _update_channel_link.delay(
        megatron_user.id, platform_user_id, request_data.response_url
    )
    return {"ok": True}
Beispiel #3
0
def clear_context(
    megatron_user_id: int, serialized_request_data: dict, arguments: dict
) -> dict:
    request_data = RequestData(**serialized_request_data)
    megatron_user = MegatronUser.objects.get(id=megatron_user_id)
    platform_user_id = User.objects.get(id=arguments["platform_user_id"])
    command_url = getattr(megatron_user, "command_url", None)
    if not command_url:
        return {"ok": False, "error": "MegatronUser has not provided a command url."}
    # TODO: Use the command that called this to standardize the 'command' param
    response = requests.post(
        megatron_user.command_url,
        json={
            "command": "clear-context",
            "platform_user_id": platform_user_id,
            "megatron_verification_token": megatron_user.verification_token,
        },
    )
    if not response.status_code == 200:
        return {"ok": False, "error": response.content}

    # TODO: This is probably better suited to being part of the integration itself
    integration = megatron_user.megatronintegration_set.first()
    integration_connection = IntegrationService(integration).get_connection(
        as_user=False
    )
    platform_username = PlatformUser.objects.get(platform_id=platform_user_id).username
    msg = {"text": f"Context cleared for *{platform_username}*."}
    integration_response = integration_connection.ephemeral_message(request_data, msg)

    if not integration_response.get("ok"):
        return {"ok": False, "error": "Failed to post confirmation to slack."}

    return {"ok": True}
Beispiel #4
0
def _check_and_send_paused_warning(megatron_channel: MegatronChannel,
                                   user_id: str):
    if megatron_channel.is_paused:
        return

    last_warning = cache.get(PAUSE_WARNING_PREFIX + str(megatron_channel.id))
    if last_warning:
        last_warning = datetime.strptime(last_warning, '%d-%m-%Y:%H:%M:%S')
        minutes_elapsed = (datetime.now() - last_warning).total_seconds() / 60
        if minutes_elapsed < TIME_TIL_NEXT_WARNING:
            return

    workspace_id = megatron_channel.workspace.platform_id
    platform_user_id = megatron_channel.platform_user_id
    msg = formatting.get_unpaused_warning(workspace_id, platform_user_id)

    channel_id = megatron_channel.platform_channel_id
    integration_connection = IntegrationService(
        megatron_channel.megatron_integration).get_connection(as_user=False)
    message_action = Action(ActionType.POST_MESSAGE, {
        'channel': channel_id,
        'message': msg
    })
    integration_connection.take_action(message_action)

    last_warning = datetime.now().strftime('%d-%m-%Y:%H:%M:%S')
    cache.set(PAUSE_WARNING_PREFIX + str(megatron_channel.id), last_warning)
Beispiel #5
0
def _image_passthrough_message(event):
    tracked_channel = MegatronChannel.objects.filter(
        platform_channel_id=event['channel']).first()
    connection = IntegrationService(
        tracked_channel.megatron_integration).get_connection(as_user=False)
    user = PlatformUser.objects.get(platform_id=event['user'])
    msg = connection.build_img_attach(event, user)
    return msg
Beispiel #6
0
def _change_pause_state(
    megatron_user: MegatronUser,
    platform_user: User,
    request_data: RequestData,
    pause=False,
) -> dict:
    workspace = platform_user.workspace
    if not getattr(megatron_user, "command_url", None):
        return {"ok": False, "error": "No command url provided for workspace."}

    customer_connection = WorkspaceService(workspace).get_connection(as_user=False)
    response = customer_connection.open_im(platform_user.platform_id)
    if not response["ok"]:
        return {
            "ok": False,
            "error": "Failed to open get im channel from "
            f"slack: {response['error']}",
        }
    channel_id = response["channel"]["id"]

    data = {
        "megatron_verification_token": settings.MEGATRON_VERIFICATION_TOKEN,
        "command": "pause",
        "channel_id": channel_id,
        "platform_user_id": platform_user.platform_id,
        "team_id": workspace.platform_id,
        "paused": pause,
    }
    response = requests.post(megatron_user.command_url, json=data)
    # TODO: This response is 200 even on failure to find user
    if not response.status_code == 200:
        return {"ok": False, "error": "Failed to pause bot for user."}

    megatron_channel = MegatronChannel.objects.get(
        workspace=workspace, platform_user_id=platform_user.platform_id
    )
    megatron_channel.is_paused = pause
    megatron_channel.save()

    # TODO: This is probably better suited to being part of the integration itself
    integration = megatron_user.megatronintegration_set.first()
    integration_connection = IntegrationService(integration).get_connection(
        as_user=False
    )
    paused_word = "paused" if pause else "unpaused"
    msg = {"text": f"Bot *{paused_word}* for user: {platform_user}."}
    channel = request_data.channel_id
    message_action = Action(
        ActionType.POST_MESSAGE, {"channel": channel, "message": msg}
    )
    integration_connection.take_action(message_action)
    return {"ok": True}
def open_channel(megatron_user_id: int, serialized_request_data: dict, arguments: dict) -> dict:
    request_data = RequestData(**serialized_request_data)
    megatron_user = MegatronUser.objects.get(id=megatron_user_id)
    platform_user_id = arguments['targeted_platform_id']
    integration = megatron_user.megatronintegration_set.first()
    connection = IntegrationService(integration).get_connection(as_user=False)
    new_msg = formatting.user_titled(platform_user_id, "Connecting....")
    response = connection.respond_to_url(request_data.response_url, new_msg)
    if not response.get('ok'):
        return response
    _update_channel_link.delay(megatron_user.id, platform_user_id,
                               request_data.response_url)
    return {'ok': True}
Beispiel #8
0
def _get_slack_user_data(channel_id, slack_id):
    # TODO: Verify responses/that user exists
    incoming_channel = MegatronChannel.objects.get(
        platform_channel_id=channel_id)
    integration_connection = IntegrationService(
        incoming_channel.megatron_integration).get_connection()
    action = Action(ActionType.GET_USER_INFO, {'user_id': slack_id})
    response = integration_connection.take_action(action)
    user_data = response['user']
    from_user = {
        'user_name': user_data['profile']['real_name'],
        'user_icon_url': user_data['profile']['image_24']
    }
    return from_user
Beispiel #9
0
def _get_slack_user_data(channel_id, slack_id):
    # TODO: Verify responses/that user exists
    incoming_channel = MegatronChannel.objects.get(
        platform_channel_id=channel_id)
    integration_connection = IntegrationService(
        incoming_channel.megatron_integration).get_connection()
    action = Action(ActionType.GET_USER_INFO, {"user_id": slack_id})
    response = integration_connection.take_action(action)
    user_data = response["user"]
    from_user = {
        "user_name": user_data["profile"]["real_name"],
        "user_icon_url": user_data["profile"]["image_24"],
    }
    return from_user
Beispiel #10
0
def unpause_reminder():
    channels = MegatronChannel.objects.all()
    for channel in channels:
        if not channel.is_paused:
            continue
        workspace_id = channel.workspace.id
        platform_user_id = channel.platform_user_id
        time_since_last_message = datetime.now() - channel.last_message_sent
        if PAUSE_WARNING_START < time_since_last_message <= PAUSE_WARNING_STOP:
            integration = MegatronIntegration.objects.get(
                megatron_user=channel.megatron_user)
            connection = IntegrationService(integration).get_connection(
                as_user=False)
            msg = formatting.get_pause_warning(workspace_id, platform_user_id)
            connection.message(channel.platform_channel_id, msg)
def _change_pause_state(megatron_user: MegatronUser, platform_user: User,
                        request_data: RequestData, pause=False
                        ) -> dict:
    workspace = platform_user.workspace
    if not getattr(megatron_user, 'command_url', None):
        return {'ok': False, 'error': 'No command url provided for workspace.'}

    customer_connection = WorkspaceService(
        workspace).get_connection(as_user=False)
    response = customer_connection.open_im(platform_user.platform_id)
    if not response['ok']:
        return {
            'ok': False,
            'error': 'Failed to open get im channel from '
            f"slack: {response['error']}"
        }
    channel_id = response['channel']['id']

    data = {
        'megatron_verification_token': settings.MEGATRON_VERIFICATION_TOKEN,
        'command': 'pause',
        'channel_id': channel_id,
        'platform_user_id': platform_user.platform_id,
        'team_id': workspace.platform_id,
        'paused': pause,
    }
    response = requests.post(megatron_user.command_url, json=data)
    # TODO: This response is 200 even on failure to find user
    if not response.status_code == 200:
        return {'ok': False, 'error': 'Failed to pause bot for user.'}

    megatron_channel = MegatronChannel.objects.get(
        workspace=workspace, platform_user_id=platform_user.platform_id)
    megatron_channel.is_paused = pause
    megatron_channel.save()

    # TODO: This is probably better suited to being part of the integration itself
    integration = megatron_user.megatronintegration_set.first()
    integration_connection = IntegrationService(
        integration).get_connection(as_user=False)
    paused_word = 'paused' if pause else 'unpaused'
    msg = {
        "text": f"Bot *{paused_word}* for user: {platform_user}."
    }
    channel = request_data.channel_id
    message_action = Action(ActionType.POST_MESSAGE, {'channel': channel, 'message': msg})
    integration_connection.take_action(message_action)
    return {'ok': True}
Beispiel #12
0
def edit(request):
    message = json.loads(request.body)
    # Warning! Currently the only way to identify msgs sent by Megatron
    try:
        footer_text = message["message"]["attachments"][-1]["footer"]
        if footer_text.startswith("sent by"):
            return OK_RESPONSE
    except KeyError:
        pass

    if message.get("user"):
        megatron_channel = MegatronChannel.objects.filter(
            platform_user_id=message["message"]["user"]).first()
    elif message.get("channel"):
        megatron_channel = MegatronChannel.objects.filter(
            platform_channel_id=message["channel"]).first()
    else:
        return OK_RESPONSE
    if not megatron_channel:
        return OK_RESPONSE
    team_connection = IntegrationService(
        megatron_channel.megatron_integration).get_connection(as_user=False)

    existing_message = MegatronMessage.objects.filter(
        customer_msg_id=message["previous_message"]["ts"],
        megatron_channel=megatron_channel,
    ).first()

    if not megatron_channel or not existing_message:
        return OK_RESPONSE

    new_message = {
        "text": message["message"].get("text", ""),
        "attachments": message["message"].get("attachments"),
    }
    old_message = {
        "channel_id": megatron_channel.platform_channel_id,
        "ts": existing_message.integration_msg_id,
    }
    params = {"new_message": new_message, "old_message": old_message}
    update_action = Action(ActionType.UPDATE_MESSAGE, params)
    response = team_connection.take_action(update_action)

    existing_message.customer_msg_id = message["message"]["ts"]
    existing_message.integration_msg_id = response["ts"]
    existing_message.save()

    return OK_RESPONSE
Beispiel #13
0
def edit(request):
    message = json.loads(request.body)
    # Warning! Currently the only way to identify msgs sent by Megatron
    try:
        footer_text = message['message']['attachments'][-1]['footer']
        if footer_text.startswith('sent by'):
            return OK_RESPONSE
    except KeyError:
        pass

    if message.get('user'):
        megatron_channel = MegatronChannel.objects.filter(
            platform_user_id=message['message']['user']).first()
    elif message.get('channel'):
        megatron_channel = MegatronChannel.objects.filter(
            platform_channel_id=message['channel']).first()
    else:
        return OK_RESPONSE
    if not megatron_channel:
        return OK_RESPONSE
    team_connection = IntegrationService(
        megatron_channel.megatron_integration).get_connection(as_user=False)

    existing_message = MegatronMessage.objects.filter(
        customer_msg_id=message['previous_message']['ts'],
        megatron_channel=megatron_channel).first()

    if not megatron_channel or not existing_message:
        return OK_RESPONSE

    new_message = {
        'text': message['message'].get('text', ''),
        'attachments': message['message'].get('attachments')
    }
    old_message = {
        'channel_id': megatron_channel.platform_channel_id,
        'ts': existing_message.integration_msg_id
    }
    params = {'new_message': new_message, 'old_message': old_message}
    update_action = Action(ActionType.UPDATE_MESSAGE, params)
    response = team_connection.take_action(update_action)

    existing_message.customer_msg_id = message['message']['ts']
    existing_message.integration_msg_id = response['ts']
    existing_message.save()

    return OK_RESPONSE
Beispiel #14
0
def _update_channel_link(
    megatron_user_id: int, platform_user_id: str, response_url: str
):
    megatron_user = MegatronUser.objects.get(id=megatron_user_id)
    integration = megatron_user.megatronintegration_set.first()
    connection = IntegrationService(integration).get_connection(as_user=False)
    platform_user = PlatformUser.objects.get(platform_id=platform_user_id)
    megatron_user = MegatronUser.objects.get(id=megatron_user_id)

    megatron_channel = MegatronChannel.objects.filter(
        platform_user_id=platform_user_id
    ).first()

    if not megatron_channel:
        username = platform_user.username + "_" + platform_user.workspace.domain
        username = re.sub(r"[^\w-]", "", username.lower())
        response = connection.create_channel(f"{settings.CHANNEL_PREFIX}{username}")
        if not response:
            response = connection.respond_to_url(
                response_url, {"text": "Error creating channel."}
            )
            if not response.get("ok"):
                LOGGER.error(f"Problem updating slack message: {response['error']}")
            return

        megatron_channel, created = _create_or_update_channel(
            megatron_user, response["channel"], platform_user
        )
        if created:
            _get_conversation_history(megatron_channel)

    elif megatron_channel.is_archived:
        MegatronChannelService(megatron_channel).unarchive()
        _get_conversation_history(megatron_channel)

    channel_link = _get_channel_link(
        megatron_user, megatron_channel.platform_channel_id
    )
    join_message = formatting.user_titled(
        platform_user_id, f"<{channel_link}|Go to slack conversation>"
    )
    response = connection.respond_to_url(response_url, join_message)
    if not response.get("ok"):
        LOGGER.error(f"Problem updating slack message: {response['error']}")
Beispiel #15
0
def notify_user(request) -> MegatronResponse:
    msg = request.data["message"]
    user_id = request.data["user_id"]
    channel_id = request.data["channel_id"]
    platform_type = request.data["platform_type"]
    request_data = RequestData(channel_id=channel_id,
                               user_id=user_id,
                               response_url="")
    platform_type = PlatformType[platform_type.capitalize()].value
    megatron_channel = MegatronChannel.objects.get(
        platform_channel_id=channel_id, workspace__platform_type=platform_type)
    connection = IntegrationService(
        megatron_channel.megatron_integration).get_connection(as_user=False)
    response = connection.ephemeral_message(request_data, msg)
    if response.get("ok"):
        return MegatronResponse({"ok": True, "track": True}, 200)
    return MegatronResponse({
        "error": response.get("error"),
        "track": False
    }, 500)
Beispiel #16
0
def notify_user(request) -> MegatronResponse:
    msg = request.data['message']
    user_id = request.data['user_id']
    channel_id = request.data['channel_id']
    platform_type = request.data['platform_type']
    request_data = RequestData(channel_id=channel_id,
                               user_id=user_id,
                               response_url="")
    platform_type = PlatformType[platform_type.capitalize()].value
    megatron_channel = MegatronChannel.objects.get(
        platform_channel_id=channel_id, workspace__platform_type=platform_type)
    connection = IntegrationService(
        megatron_channel.megatron_integration).get_connection(as_user=False)
    response = connection.ephemeral_message(request_data, msg)
    if response.get('ok'):
        return MegatronResponse({'ok': True, 'track': True}, 200)
    return MegatronResponse({
        'error': response.get('error'),
        'track': False
    }, 500)
Beispiel #17
0
def get_a_human(request):
    payload = json.loads(request.body)
    requesting_user = payload['requesting_user']
    workspace_id = payload['workspace_id']
    integration = MegatronIntegration.objects.get(
        megatron_user__id=request.user.id)
    team_connection = IntegrationService(integration).get_connection(
        as_user=False)

    # TODO: This should all be integration dependent
    channel = settings.NOTIFICATIONS_CHANNELS[
        NotificationChannels.customer_service]

    slack_name = requesting_user['with_team_domain']
    attach = {
        "color":
        '1f355e',
        "text":
        f"🆘 *{slack_name}* requested some help!",
        "callback_id":
        f"open",
        "actions": [{
            "name": "categorize",
            "text": "Open engagement channel",
            "type": "button",
            "value": f'{workspace_id}-{requesting_user["slack_id"]}'
        }],
        "mrkdwn_in": ["text"]
    }
    msg = ({'text': ' ', 'attachments': [attach]})

    msg_action = Action(ActionType.POST_MESSAGE, {
        'channel': channel,
        'message': msg
    })
    response = team_connection.take_action(msg_action)
    if response.get('ok'):
        return OK_RESPONSE
    return MegatronResponse(response.get('error'), response.get('status'))
Beispiel #18
0
def incoming(request) -> MegatronResponse:
    msg = request.data['message']
    megatron_user = request.user
    integration = megatron_user.megatronintegration_set.first()
    team_interpreter = IntegrationService(integration).get_interpreter()
    channel = MegatronChannel.objects.filter(
        platform_user_id=msg['user']).first()
    if not channel or channel.is_archived:
        return MegatronResponse({'ok': True, 'track': False}, 200)
    response = team_interpreter.incoming(msg, channel)
    if response.get('ok'):

        if response.get('watched_channel'):
            front_integration.incoming_message.delay(msg)
            channel = MegatronChannel.objects.filter(
                platform_user_id=request.data['message']['user']).first()
            MegatronMessage.objects.create(
                integration_msg_id=response['ts'],
                customer_msg_id=request.data['message']['ts'],
                megatron_channel=channel)

        return OK_RESPONSE
    return MegatronResponse(response.get('error'), 500)
Beispiel #19
0
def incoming(request) -> MegatronResponse:
    msg = request.data["message"]
    megatron_user = request.user
    integration = megatron_user.megatronintegration_set.first()
    team_interpreter = IntegrationService(integration).get_interpreter()
    channel = MegatronChannel.objects.filter(
        platform_user_id=msg["user"]).first()
    if not channel or channel.is_archived:
        return MegatronResponse({"ok": True, "track": False}, 200)
    response = team_interpreter.incoming(msg, channel)
    if response.get("ok"):

        if response.get("watched_channel"):
            front_integration.incoming_message.delay(msg)
            channel = MegatronChannel.objects.filter(
                platform_user_id=request.data["message"]["user"]).first()
            MegatronMessage.objects.create(
                integration_msg_id=response["ts"],
                customer_msg_id=request.data["message"]["ts"],
                megatron_channel=channel,
            )

        return OK_RESPONSE
    return MegatronResponse(response.get("error"), 500)
Beispiel #20
0
def outgoing(request) -> MegatronResponse:
    user_id = request.data["user"]
    message = request.data["message"]
    try:
        message["attachments"] = json.loads(message["attachments"])
    except TypeError:
        pass
    try:
        megatron_channel = MegatronChannel.objects.get(
            platform_user_id=user_id)
    except MegatronChannel.DoesNotExist:
        return MegatronResponse({"ok": True, "track": False}, 200)
    if megatron_channel.is_archived:
        return MegatronResponse({"ok": True, "track": False}, 200)

    megatron_integration = megatron_channel.megatron_integration
    interpreter = IntegrationService(megatron_integration).get_interpreter()
    customer_workspace_id = megatron_channel.workspace.platform_id
    response = interpreter.outgoing(message, megatron_channel)
    if response.get("ok"):
        if response.get("watched_channel"):
            front_integration.outgoing_message.delay(user_id,
                                                     customer_workspace_id,
                                                     message)
            megatron_msg = MegatronMessage(
                integration_msg_id=response["ts"],
                customer_msg_id=request.data["ts"],
                megatron_channel=megatron_channel,
            )
            megatron_msg.save()

        return MegatronResponse({"ok": True, "track": True}, 200)
    return MegatronResponse({
        "error": response.get("error"),
        "track": False
    }, 500)
def _get_conversation_history(channel: MegatronChannel):
    connection = WorkspaceService(channel.workspace).get_connection()
    response = connection.open_im(channel.platform_user_id)
    channel_id = response['channel']['id']
    prev_messages = connection.im_history(channel_id, 10)
    integration_interpreter = IntegrationService(
        channel.megatron_integration).get_interpreter()
    messages = prev_messages['messages']
    messages.sort(key=lambda message: message['ts'])
    previous_ts = None
    for message in messages:
        timestamp = datetime.fromtimestamp(int(message['ts'].split('.')[0]))
        formatted_timestamp = _format_slack_timestamp(timestamp, previous_ts)
        message['text'] = f"{formatted_timestamp}{message['text']}"
        previous_ts = timestamp

        if message.get('bot_id'):
            integration_interpreter.outgoing(message, channel)
        else:
            integration_interpreter.incoming(message, channel)
    return prev_messages