from django.contrib.auth.decorators import permission_required, login_required
from django.shortcuts import reverse, render, get_object_or_404
from django.http import HttpResponseRedirect
from .models import ReportedMessage
from .api import lookup_user, user_profile, message_link, channel_info
# Slack Management Views
[docs]@login_required
@permission_required('slack.view_reportedmessage', raise_exception=True)
def report_list(request):
"""
View Slack messages reported for moderation
"""
reports = ReportedMessage.objects.filter(resolved=False)
if request.method == 'POST':
report_id = request.POST['report_id']
report = ReportedMessage.objects.get(pk=report_id)
report.resolved = True
report.save()
# message_unreact(report.message.posted_to, report.message.ts, 'triangular_flag_on_post')
return HttpResponseRedirect(reverse('slack:moderate'))
return render(request, 'slack/slack_message_list.html', {'title': 'Reported Messages', 'reports': reports})
[docs]@login_required
@permission_required('slack.view_reportedmessage', raise_exception=True)
def view_report(request, pk):
"""
View full report for a Slack message that has been flagged for moderation
"""
report = get_object_or_404(ReportedMessage, pk=pk)
if request.method == 'POST':
report.resolved = True
report.save()
# message_unreact(report.message.posted_to, report.message.ts, 'triangular_flag_on_post')
return HttpResponseRedirect(reverse('slack:moderate'))
posted_to = report.message.posted_to
info = channel_info(posted_to)
if info:
posted_to = '#' + info['name']
slack_message = report.message.content
if report.message.blocks:
slack_message = "<a href='https://wpilnl.slack.com/app_redirect?channel=" + \
report.message.posted_to + "'>Click here to view message</a>"
context = {
'posted_by': report.message.posted_by,
'posted_to': posted_to,
'report': report,
'reported_by': report.reported_by,
'message': slack_message
}
return render(request, 'slack/slack_message_report.html', context)
# Block Kit Views
[docs]def generate_modal(title, callback_id, blocks):
"""
Generate a modal view object using Slack's BlockKit
:param title: Title to display at the top of the modal view
:param callback_id: Identifier used to help determine the type of modal view in future responses
:param blocks: Blocks to add to the modal view
:return: View object (Dictionary)
"""
modal = {
"type": "modal",
"callback_id": callback_id,
"title": {
"type": "plain_text",
"text": title,
"emoji": False
},
"submit": {
"type": "plain_text",
"text": "Submit",
"emoji": False
},
"close": {
"type": "plain_text",
"text": "Cancel",
"emoji": False
},
"blocks": blocks
}
return modal
[docs]def tfed_modal():
"""
Blocks for the TFed ticket submission form.
Generated using the Block Kit Builder (https://app.slack.com/block-kit-builder)
"""
blocks = [
{
"type": "input",
"block_id": "subject",
"element": {
"type": "plain_text_input",
"action_id": "subject-action",
"placeholder": {
"type": "plain_text",
"text": "Short Summary",
"emoji": False
},
"max_length": 100
},
"label": {
"type": "plain_text",
"text": "Subject",
"emoji": False
}
},
{
"type": "input",
"block_id": "description",
"element": {
"type": "plain_text_input",
"action_id": "description-action",
"placeholder": {
"type": "plain_text",
"text": "What can we help you with today?",
"emoji": False
},
"multiline": True
},
"label": {
"type": "plain_text",
"text": "Please describe your problem or request, in detail",
"emoji": False
}
},
{
"type": "input",
"block_id": "rt_topic",
"element": {
"type": "static_select",
"placeholder": {
"type": "plain_text",
"text": "Select",
"emoji": False
},
"options": [
{
"text": {
"type": "plain_text",
"text": "Web Services",
"emoji": False
},
"value": "Database"
},
{
"text": {
"type": "plain_text",
"text": "Equipment Repairs",
"emoji": False
},
"value": "Repairs"
}
],
"action_id": "rt_topic-action"
},
"label": {
"type": "plain_text",
"text": "Topic",
"emoji": False
}
}
]
return generate_modal('New TFed Ticket', 'tfed-modal', blocks)
[docs]def tfed_ticket(ticket):
"""
Generate blocks for TFed ticket response message.
Generated using the Block Kit Builder (https://app.slack.com/block-kit-builder)
"""
ticket_assignee = 'Nobody'
if ticket.get('assignee', None):
ticket_assignee = '@' + ticket['assignee']
blocks = [
{
"type": "section",
"block_id": ticket['id'] + '~' + ticket['reporter'],
"text": {
"type": "mrkdwn",
"text": "\n*<" + ticket['url'] + "|Ticket #" + ticket['id'] + ": " + ticket['subject'] + ">*"
}
},
{
"type": "section",
"text": {
"type": "plain_text",
"text": ticket['description'],
"emoji": False
}
},
{
"type": "divider"
},
{
"type": "context",
"elements": [
{
"type": "mrkdwn",
"text": "*Status* » " + ticket['status'] + "\n*Assignee* » " + ticket_assignee +
"\n*Reporter* » @" + ticket['reporter']
}
]
},
{
"type": "actions",
"block_id": "ticket-actions",
"elements": []
}
]
ticket_status = ticket['status']
if ticket_status == "New":
blocks[4]['elements'].append(generate_button("Open Ticket", "open", "primary", action_suffix="ticket"))
elif ticket_status not in ["Resolved", "Deleted", "Rejected"]:
blocks[4]['elements'].append(generate_button("Update Ticket", "update", action_suffix="ticket"))
blocks[4]['elements'].append(generate_button("Close Ticket", "close", "danger", action_suffix="ticket"))
else:
del blocks[4]
return blocks
[docs]def ticket_update_modal(ticket_id, channel, timestamp, action):
"""
Blocks for the TFed ticket update form.
Generated using the Block Kit Builder (https://app.slack.com/block-kit-builder)
:param ticket_id: The ticket # for the ticket being updated
:param channel: The channel the ticket is being updated from
:param timestamp: The timestamp of the message that triggered this action
:param action: The type of update operation (Options: 'open', 'update', 'close')
"""
blocks = [
{
"type": "section",
"block_id": "ticket_status",
"text": {
"type": "mrkdwn",
"text": "*Ticket Status*:"
},
"accessory": {
"type": "static_select",
"placeholder": {
"type": "plain_text",
"text": "Select an item",
"emoji": False
},
"options": [
{
"text": {
"type": "plain_text",
"text": "New",
"emoji": False
},
"value": "new"
},
{
"text": {
"type": "plain_text",
"text": "Open",
"emoji": False
},
"value": "open"
},
{
"text": {
"type": "plain_text",
"text": "Stall",
"emoji": False
},
"value": "stalled"
},
{
"text": {
"type": "plain_text",
"text": "Resolve",
"emoji": False
},
"value": "resolved"
},
{
"text": {
"type": "plain_text",
"text": "Reject",
"emoji": False
},
"value": "rejected"
},
],
"initial_option": {
"text": {
"type": "plain_text",
"text": "Open",
"emoji": False
},
"value": "open"
},
"action_id": "ticket_status-action"
}
},
{
"type": "section",
"block_id": "ticket_assignee",
"text": {
"type": "mrkdwn",
"text": "*Assignee*:"
},
"accessory": {
"type": "users_select",
"placeholder": {
"type": "plain_text",
"text": "Select a user",
"emoji": False
},
"action_id": "ticket_assignee-action"
},
},
{
"type": "divider",
"block_id": ticket_id + "#" + channel + "#" + timestamp,
},
{
"type": "input",
"block_id": "ticket_comment",
"element": {
"type": "plain_text_input",
"action_id": "ticket_comment-action",
"multiline": True
},
"label": {
"type": "plain_text",
"text": "Comments",
"emoji": False
},
"optional": True
},
{
"type": "actions",
"block_id": "email_requestor",
"elements": [
{
"type": "checkboxes",
"options": [
{
"text": {
"type": "plain_text",
"text": "Send to requestor",
"emoji": False
},
"value": "send-email"
}
],
"action_id": "email_requestor-action"
}
]
}
]
if action == "open-ticket":
del blocks[0]["accessory"]["options"][0]
elif action == "update-ticket":
del blocks[0]["accessory"]["initial_option"]
else:
del blocks[1]
blocks[0]["accessory"]["initial_option"]["text"]["text"] = "Resolve"
blocks[0]["accessory"]["initial_option"]["value"] = "resolved"
return generate_modal("Update Ticket", "ticket-update-modal", blocks)
[docs]def welcome_message():
"""
Blocks for the Welcome Message. This message will be displayed to new users joining the Slack workspace.
Generated using the Block Kit Builder (https://app.slack.com/block-kit-builder)
"""
blocks = [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "Welcome to the LNL Slack!",
"emoji": False
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "We use Slack pretty heavily to communicate with one another (typically for more informal "
"communications). Here are some helpful tips and reminders to help you get started:"
}
},
{
"type": "divider"
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Default Channels*\n\n• #general - Used to announce general *LNL-related* information to the "
"entire club.\n\n• #random - If you would like to share anything that is not directly relevant "
"to our normal business, post it here.\n\n• #work-announcements - This is, as you may have "
"guessed, where we post work announcements for setups and strikes."
}
},
{
"type": "divider"
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Tips and Tricks*\n\n>*Please use threads when replying to messages!* This helps us keep the "
"number of notifications we all receive to a respectable level.\n\n>You can mention particular "
"users in your messages to get their attention. For instance, you could mention @lnl to notify "
"the LNL Laptop.\n\n>If your message is really important, you can get the attention of "
"everyone in a channel using @channel; however when possible, we recommend using @here instead."
}
},
{
"type": "divider"
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": "Need Help?",
"emoji": False
},
"style": "primary",
"url": "https://lnl.wpi.edu/help",
"action_id": "welcome-help"
}
]
}
]
return blocks
[docs]def app_home(tickets):
"""
Blocks for the App's Home tab.
Generated using the Block Kit Builder (https://app.slack.com/block-kit-builder)
:param tickets: A list of ticket ids
"""
title = "*My Recent Tickets*"
if len(tickets) == 0:
title = "*You haven't submitted any tickets yet*"
blocks = [
{
"type": "context",
"elements": [
{
"type": "mrkdwn",
"text": title
}
]
},
{
"type": "divider"
}
]
for ticket in tickets:
blocks.append(
{
"type": "section",
"block_id": str(ticket['id']),
"text": {
"type": "mrkdwn",
"text": "\n*Ticket #" + str(ticket['id']) + ": " + ticket['Subject'] + "*\nStatus » " +
ticket['Status'].capitalize()
},
"accessory": {
"type": "overflow",
"options": [
{
"text": {
"type": "plain_text",
"text": "Comment",
"emoji": False
},
"value": "Comment"
}
],
"action_id": "home-ticket-update"
}
}
)
blocks.append({"type": "divider"})
return blocks
[docs]def report_message_modal(message):
"""
Blocks for the modal view that is displayed when a user reports a problematic Slack message.
Generated using the Block Kit Builder (https://app.slack.com/block-kit-builder)
:param message: A SlackMessage object representing the message
"""
blocks = [
{
"type": "section",
"block_id": str(message.pk),
"text": {
"type": "plain_text",
"text": "This message will be flagged and sent to a moderator for review.",
"emoji": False
}
},
{
"type": "input",
"block_id": "report-comment",
"element": {
"type": "plain_text_input",
"multiline": True,
"action_id": "comment-action"
},
"label": {
"type": "plain_text",
"text": "Comments",
"emoji": False
},
"optional": True
}
]
return generate_modal('Report a message', 'report-modal', blocks)
[docs]def reported_message_notification(sender, report):
"""
Blocks for the notification sent to the Webmaster whenever a new report is submitted.
Generated using the Block Kit Builder (https://app.slack.com/block-kit-builder)
:param sender: The Slack ID for the user that sent the report
:param report: The corresponding ReportedMessage object
"""
reporter = "Someone"
slack_user = user_profile(sender)
if slack_user['ok']:
reporter = "@" + slack_user['user']['name']
link = message_link(report.message.posted_to, report.message.ts)
blocks = [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "%s has flagged a new message for review by a moderator." % reporter
}
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": ":slack: View in Slack",
"emoji": True
},
"style": "primary",
"url": link,
"action_id": "reported-message-slack"
},
{
"type": "button",
"text": {
"type": "plain_text",
"text": "View Report",
"emoji": False
},
"url": "https://lnl.wpi.edu" + reverse("slack:report", args=[report.pk]),
"action_id": "reported-message-report"
}
]
}
]
if not link:
del blocks[1]['elements'][0]
return blocks
[docs]def cc_add_notification(cci):
"""
Blocks for a Crew Chief add notification.
Generated using the Block Kit Builder (https://app.slack.com/block-kit-builder)
:param cci: EventCCInstance object
"""
blocks = [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "You've been added as a crew chief to the event *%s*. Your setup is currently scheduled for "
"*%s* in the *%s*." % (cci.event.event_name, cci.setup_start.strftime('%b %-d, %Y at %-I:%M %p'),
cci.setup_location.name.strip())
}
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": "Event Details",
"emoji": False
},
"style": "primary",
"url": "https://lnl.wpi.edu" + reverse("events:detail", args=[cci.event.pk]),
"action_id": "cc-add-%s" % cci.event.pk
},
{
"type": "button",
"text": {
"type": "plain_text",
"text": ":calendar: Add to calendar",
"emoji": True
},
"url": "https://lnl.wpi.edu" + reverse("events:ics", args=[cci.event.pk]),
"action_id": "ics-download-%s" % cci.event.pk
},
{
"type": "button",
"text": {
"type": "plain_text",
"text": ":page_facing_up: Submit CC Report",
"emoji": True
},
"url": "https://lnl.wpi.edu" + reverse("my:report", args=[cci.event.pk]),
"action_id": "cc-report-%s" % cci.event.pk
}
]
}
]
return blocks
[docs]def cc_report_reminder(cci):
"""
Blocks for a missing crew chief report reminder
Generated using the Block Kit Builder (https://app.slack.com/block-kit-builder)
:param cci: EventCCInstance object
"""
blocks = [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "CC Report Reminder",
"emoji": False
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "This is a reminder that you have a pending crew chief report for *%s*.\n\n\n"
"Submitting a crew chief report and recording crew hours are required parts of being a crew "
"chief. These tasks are expected to be completed shortly after an event while you still have "
"all the details of the event fresh in your mind. Delaying submitting your crew chief report "
"directly delays the billing of this event." % cci.event.event_name
}
},
{
"type": "divider"
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": ":page_facing_up: Submit Report",
"emoji": True
},
"style": "primary",
"url": "https://lnl.wpi.edu" + reverse("my:report", args=[cci.event.pk]),
"action_id": "cc-report-%s" % cci.event.pk
}
]
}
]
return blocks
[docs]def event_edited_notification(event, triggered_by, fields_changed):
"""
Blocks for an event edited Slack notification.
Generated using the Block Kit Builder (https://app.slack.com/block-kit-builder)
:param event: The event object
:param triggered_by: The user that edited the event
:param fields_changed: A list of fields that have changed
"""
user = triggered_by.get_full_name()
slack_user = user_profile(lookup_user(triggered_by.email))
if slack_user['ok']:
user = "@" + slack_user['user']['name']
blocks = [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "The following event was just edited by %s" % user
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Fields changed*: %s\n\n> *%s*\n> _%s_\n> *Start*: %s\n> *End*: %s\n> *Services*: %s\n"
"> *Client*: %s" % (', '.join(fields_changed), event.event_name, event.location,
event.datetime_start.strftime('%b %-d, %Y, %-I:%M %p'),
event.datetime_end.strftime('%b %-d, %Y, %-I:%M %p'), event.short_services,
event.org_to_be_billed)
}
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": "View Event",
"emoji": False
},
"style": "primary",
"url": "https://lnl.wpi.edu" + reverse('events:detail', args=[event.pk]),
"action_id": "edited-event-%s" % str(event.pk)
}
]
}
]
return blocks