def decorated(bot, channel, *args):

        if bot.bot_id not in _last_answer or channel not in _last_answer[bot.bot_id]:
            send_slack_message(bot.bot_token, channel, "Please ask a question first.")
            return
        else:
            return wrapped(bot, channel, *args)
def _answer(bot, channel, request, question):
    user = User.get('user_id', bot.user_id)
    request['args']['token'] = user.token
    request['args']['question'] = question
    request['args']['numberofitems'] = '5'
    response = _process_responder_api(bot, channel, responder_answer, request)
    if not response:
        return
    answers = response['result']['items']
    if bot.bot_id not in _previous_answers:
        _previous_answers[bot.bot_id] = {}
        _last_answer[bot.bot_id] = {}
    _previous_answers[bot.bot_id][channel] = answers
    _last_answer[bot.bot_id][channel] = 0
    _LAST_QUESTION[bot.bot_id, channel] = question
    if not answers or answers[0]['confidence'] < NUMERICAL_EXPRESSION_THRESHOLD:
        numerical_answer = try_numerical_answer(question)
        if numerical_answer:
            answers.insert(0, {"answerText": numerical_answer[0] + "=" + numerical_answer[1],
                               "confidence": 0.80,
                               "sourceType": "numerical",
                               "sourceId": str(uuid4()),
                               "matchedQuestion": f"What is {numerical_answer[0]} ?"
                               })
    if len(answers) == 0:
        send_slack_message(bot.bot_token, channel, "Sorry! I don't know the answer to that.")
    else:
        _process_answer(bot, channel, question, answers[0])
        response = send_slack_message(bot.bot_token, channel, answers[0]['answerText'])
        _BOT_TS_TO_MESSAGE[bot.bot_id, channel, response['message']['ts']] = answers[0]['answerText']
def _add_saved_reply(bot, channel, request, message):
    try:
        if message.startswith("."):
            message = message[NON_WORD_CHARS.search(message).end():]
        question_answer = [qa.strip() for qa in message.split('|')]
        if len(question_answer) < 2:
            raise IndexError()
    except IndexError:
        send_slack_message(bot.bot_token, channel,
                           "Sorry, I didn't understand that. The usage for `.add` is: "
                           ".add question | answer")
        return

    request['user'] = User.get('user_id', bot.user_id)
    questions = question_answer[:-1]
    answer = question_answer[-1]
    request['args']['question'] = questions[0]
    request['args']['answer'] = answer
    response = _process_responder_api(bot, channel, responder_create_saved_reply, request)
    if not response:
        return
    reply_id = response['result']['replyId']
    for question in questions[1:]:
        request['args']['question'] = question
        request['args']['replyid'] = reply_id  # we do lower() for all parameters
        if not _process_responder_api(bot, channel, responder_add_paraphrase_question, request):
            return
    if len(questions) == 1:
        questions_text = f'_{questions[0]}_\n'
    else:
        questions_text = ''
        for question in questions:
            questions_text += f'•_{question}_\n'
    send_slack_message(bot.bot_token, channel,
                       f"Thanks, I'll remember that:\n{questions_text}>>>{answer}")
def _next(bot, channel, *args):
    next_answer = _last_answer[bot.bot_id][channel] + 1
    if next_answer < len(_previous_answers[bot.bot_id][channel]):
        answer = _previous_answers[bot.bot_id][channel][next_answer]
        _process_answer(bot, channel, _LAST_QUESTION[bot.bot_id, channel], answer)
        response = send_slack_message(bot.bot_token, channel, answer['answerText'])
        _BOT_TS_TO_MESSAGE[bot.bot_id, channel, response['message']['ts']] = answer['answerText']
        _last_answer[bot.bot_id][channel] = next_answer
    else:
        send_slack_message(bot.bot_token, channel, "I'm afraid I've run out of answers to that question.")
def _process_responder_api(bot, channel, api_endpoint, request) -> Optional[dict]:
    try:
        response = json.loads(api_endpoint(request).body)
    except UserException as e:
        send_slack_message(bot.bot_token, channel, e.message + ERROR_HELP_MESSAGE)
        return None
    if response['success']:
        return response
    else:
        send_slack_message(bot.bot_token, channel, response['result']['message'] + ERROR_HELP_MESSAGE)
        return None
def _explain(bot, channel, *args):
    previous = _get_last_answer(bot, channel)
    if previous['sourceType'] == 'document':
        context = previous["answerContext"]
        local_start_offset = previous['answerTextStartOffset'] - previous['answerContextStartOffset']
        local_end_offset = previous['answerTextEndOffset'] - previous['answerContextStartOffset']
        bold_text = context[local_start_offset:local_end_offset].replace('\n', '')
        context = f"{context[:local_start_offset]} *{bold_text}* {context[local_end_offset:]}"
        send_slack_message(bot.bot_token, channel,
                           f"From _{previous['sourceId']}_ (Index {previous['confidence']:.2f})\n>>>{context}")
    else:
        send_slack_message(bot.bot_token, channel,
                           f"I thought you asked (Index {previous['confidence']:.2f})\n_{previous['matchedQuestion']}_\n>>>{previous['answerText']}")
def _help(bot, channel, *args):
    send_slack_message(bot.bot_token, channel, """Hi, I am *Capebot*, I will answer all your questions, I will learn from you and your documents and improve over time.
Here are my commands:

    *.add* _question_ | _answer_ - Create a new saved reply.
    *.next* - Show the next possible answer for the last question.
    *.why* - Explain why the last answer was given.
    *.help* - Display this message.

You can also :

    *Add* a Slack emoji reaction to bot answers with :thumbsup: or :smiley:, I will remember and improve over time. 
    *Upload* text and markdown documents by sending them to me in a private message. I will read them when answering.
    *Ask* me to calculate, for example `what is 3+2?`.

For more options login to your account at https://thecape.ai.
    """)
def _process_positive_reaction(bot: Bot, request, event: dict) -> Optional[bool]:
    if event['type'] != 'reaction_added':
        return None
    if event['reaction'] not in {'smiley', 'smile', 'wink', 'simple_smile', 'grinning', 'kissing', 'laughing',
                                 'satisfied', 'thumbsup', 'ok_hand', '+1', 'v', 'point_up', 'point_up_2', 'clap',
                                 'muscle', 'raised_hands', 'arrow_up', 'up', 'ok', 'new', 'top', 'cool', '100',
                                 'heavy_check_mark', 'ballot_box_with_check', 'white_check_mark'}:
        return False
    channel = event['item']['channel']
    question_answer = _get_question_answer(bot, channel, event['item']['ts'])
    if not question_answer:
        return None
    _remove_question_answer(bot, channel, event['item']['ts'])
    question = question_answer['question']
    last_answer = question_answer['answer']
    if last_answer['sourceType'] == 'saved_reply':
        if last_answer['confidence'] == 1.0:
            send_slack_message(bot.bot_token, channel,
                               f"Thanks for the feedback.\n_{question}_\n>>>{last_answer['answerText']}")
            return True
        request['user'] = User.get('user_id', bot.user_id)
        request['args']['question'] = question.strip()
        request['args']['replyid'] = last_answer['sourceId']  # we do lower() for all parameters
        if _process_responder_api(bot, channel, responder_add_paraphrase_question, request):
            send_slack_message(bot.bot_token, channel,
                               f"Thanks, I'll remember that:\n_{question}_\n>>>{last_answer['answerText']}")
        return True
    elif last_answer['sourceType'] == 'document':
        request['user'] = User.get('user_id', bot.user_id)
        request['args']['question'] = question.strip()
        request['args']['answer'] = last_answer['answerText'].strip()
        if _process_responder_api(bot, channel, responder_create_saved_reply, request):
            send_slack_message(bot.bot_token, channel,
                               f"Thanks, I'll remember that:\n_{question}_\n>>>{last_answer['answerText']}")
        return True
    else:
        send_slack_message(bot.bot_token, channel,
                           f"Thanks for the feedback.\n_{question}_\n>>>{last_answer['answerText']}")
        return True
def process_file(event, request):
    authed_users = required_parameter(request, 'authed_users')
    bot_id = authed_users[0]
    channel = event['channel']
    bot = Bot.get('bot_id', bot_id)
    slack_file = event['file']
    if slack_file['filetype'] not in ['text', 'markdown']:
        send_slack_message(bot.bot_token, channel, ERROR_FILE_TYPE_UNSUPPORTED)
    else:
        text = get_slack_file_contents(bot.bot_token, slack_file['url_private'])
        try:
            request['user'] = User.get('user_id', bot.user_id)
            request['args']['text'] = text
            request['args']['title'] = slack_file['title']
            request['args']['origin'] = slack_file['name']
            request['args']['replace'] = 'true'
            responder_upload_document(request)
            send_slack_message(bot.bot_token, channel, BOT_FILE_UPLOADED)
        except UserException as e:
            send_slack_message(bot.bot_token, channel, e.message)
def _echo(bot, channel, request, message):
    if message.startswith(".echo"):
        _ECHO_MODE[bot.bot_id, channel] = not _ECHO_MODE.get((bot.bot_id, channel), False)
        send_slack_message(bot.bot_token, channel, "Echo mode toggled")
    else:
        send_slack_message(bot.bot_token, channel, message)