def slack_dialog(request): # noqa: C901 json_payload, slack_settings = validate_slack_request(request) # An error occured in validate_slack_response. So we return a 200 # and tell Slack to not retry. if slack_settings is None: response = HttpResponse(status=200) response["X-Slack-No-Retry"] = "1" return response customer = slack_settings.customer channel_id = json_payload["channel"]["id"] payload_type = json_payload["type"] headers = {"Content-Type": "application/json; charset=utf-8"} if payload_type == "block_actions": # Yes or No button has been clicked if json_payload["actions"][0]["text"]["text"] == "No": # "No" Button clicked - delete ephemeral message delete_ephemeral_message( json_payload["container"]["message_ts"], json_payload["response_url"], headers, ) else: # "Yes" button clicked - show dialog trigger_id = json_payload["trigger_id"] # message_ts is the pointer to the message we care about adding to Savio, NOT the timestamp # of the response to the Yes/No ephemeral message that just got POSTed message_ts = json_payload["actions"][0]["value"] # Get Message so we can display "problem" as a default in the Dialog's problem textarea message_url = "https://slack.com/api/channels.history" message_json = { "oldest": message_ts, "count": 1, "inclusive": True, "channel": channel_id, } headers = { "Content-Type": "application/x-www-form-urlencoded;", "Authorization": "Bearer " + slack_settings.slack_user_access_token, } response = requests.post(message_url, data=message_json, headers=headers) if not response.json()["ok"]: capture_message( f"Error getting Slack Message: message.json: {response.json()}. slack_settings_id: {slack_settings.id}." ) response = HttpResponse(status=200) response["X-Slack-No-Retry"] = "1" return response else: message = response.json()["messages"][0]["text"] dialog_json = get_dialog_json(trigger_id, "show_create_feedback_dialog", message_ts, message, customer) headers = { "Content-Type": "application/json; charset=utf-8", "Authorization": "Bearer " + slack_settings.slack_bot_token, } dialog_url = "https://slack.com/api/dialog.open" response = requests.post(dialog_url, data=json.dumps(dialog_json), headers=headers) if not response.json()["ok"]: capture_message( f"Error generating Slack dialog: response.json: {response.json()}. message: {message}. message_ts: {message_ts}. slack_settings_id: {slack_settings.id}" ) response = HttpResponse(status=200) response["X-Slack-No-Retry"] = "1" return response delete_ephemeral_message( json_payload["container"]["message_ts"], json_payload["response_url"], headers, ) else: # Dialog initiated by message action callback_id = json_payload["callback_id"] if callback_id == "show_create_feedback_dialog": if payload_type == "dialog_submission": # Saving a Posted Dialog slack_user_name = json_payload["user"]["name"] slack_user_id = json_payload["user"]["id"] problem = json_payload["submission"]["problem"] person = json_payload["submission"]["person"] new_person_email = json_payload["submission"][ "new_person_email"] feedback_from = json_payload["submission"]["feedback_from"] feature_request = json_payload["submission"]["feature_request"] response_url = json_payload["response_url"] # Validate submitted data errors = {"errors": []} if not problem: errors["errors"].append({ "name": "problem", "error": "Can't be blank. Please try again.", }) if not feedback_from: errors["errors"].append({ "name": "feedback_from", "error": "Can't be blank. Please try again.", }) if not (person or new_person_email): errors["errors"].append({ "name": "person", "error": "You need to select an existing or new person.", }) errors["errors"].append({ "name": "new_person_email", "error": "You need to select an existing or new person.", }) if len(errors["errors"]) > 0: capture_message( f"Invalid params submitted from Slack. problem: {problem}. person: {person}. feedback_from: {feedback_from}" ) return JsonResponse(errors) # To post to the Slack channel, we first need to get the permalink of the parent message. permalink_url = "https://slack.com/api/chat.getPermalink" shared_ts = json_payload["state"] # Add Auth header to headers. We don't need it for previous posts to response_url, but we do # post web API methods like chat.getPermalink headers[ "Authorization"] = "Bearer " + slack_settings.slack_bot_token permalink_params = { "channel": channel_id, "message_ts": shared_ts } permalink_response = requests.post(permalink_url, params=permalink_params, headers=headers).json() if not permalink_response["ok"]: params = { "text": "There was an error saving your feedback. Please try again.", } requests.post(response_url, data=json.dumps(params), headers=headers) capture_message( f"Invalid permalink from Slack. channel: {channel_id}. message timestamp: {shared_ts}. " ) return HttpResponse(status=406) message_permalink = permalink_response["permalink"] # Look up User. The user in Slack likely won't have a row in our users table. if slack_settings.slack_user_id == slack_user_id: u = slack_settings.user else: u = None # Are we creating a new person, or using an existing one? Figure it out. if person: use_person_id = person else: # handle case where email entered but user exists. try: user = AppUser.objects.get(email=new_person_email, customer_id=customer.id) except AppUser.DoesNotExist: user = AppUser.objects.create(email=new_person_email, customer_id=customer.id) use_person_id = user.id # Save feedback to DB feedback = Feedback( customer=customer, source_url=message_permalink, problem=problem, feedback_type=feedback_from, feature_request_id=feature_request, user_id=use_person_id, source_username=slack_user_name, created_by=u, ) feedback.save() if u: user_id = u.id else: user_id = f"Slack - {slack_user_id}" tracking.feedback_created(user_id, customer, feedback, tracking.EVENT_SOURCE_SLACK) # Then, we'll post a reply to the message as part of a thread post_message_url = "https://slack.com/api/chat.postMessage" now = datetime.datetime.now() unix_time = now.timestamp() savio_feedback_url = settings.HOST + feedback.get_absolute_url( ) date_string = ("<!date^" + repr(int(unix_time)) + "^{date} at {time}^" + savio_feedback_url + "|" + now.strftime("%b %d %Y at %I:%M %p") + ">") company_str = "" if feedback.user.company: company_str = f" @ {feedback.user.company.name}" fr_string = "None" if feedback.feature_request is not None: fr_url = settings.HOST + reverse( "feature-request-feedback-details", kwargs={"pk": feedback.feature_request.id}, ) fr_string = f"<{fr_url}|{feedback.feature_request.title}>" # This Code creates a payload to reply in a thread with the original message as the parent reply_json = { "channel": channel_id, "as_user": False, "link_names": True, "mrkdwn": True, "unfurl_links": True, "thread_ts": shared_ts, "blocks": [ { "type": "context", "elements": [ { "type": "mrkdwn", "text": f"@{slack_user_name} pushed this customer feedback to Savio on {date_string}:", }, ], }, { "type": "section", "text": { "type": "mrkdwn", "text": f"*From*\n{feedback.user.get_name_or_email()}{company_str} ({dict(feedback.TYPE_CHOICES)[feedback.feedback_type]})\n\n*Feedback*\n{problem}\n\n*Feature Request*\n{fr_string}", }, }, ], } # End Code posts a response to the Slack Channel with the message posted to Savio response = requests.post(post_message_url, data=json.dumps(reply_json), headers=headers) elif payload_type == "message_action": # Show a Dialog trigger_id = json_payload["trigger_id"] message = json_payload["message"]["text"] message_ts = json_payload["message_ts"] dialog_json = get_dialog_json(trigger_id, callback_id, message_ts, message, customer) headers = { "Content-Type": "application/json; charset=utf-8", "Authorization": "Bearer " + slack_settings.slack_bot_token, } dialog_url = "https://slack.com/api/dialog.open" response = requests.post(dialog_url, data=json.dumps(dialog_json), headers=headers) return Response(status=status.HTTP_200_OK)