-
-
Save one-data-cookie/6fb47a434d9bf29730beb74220231af2 to your computer and use it in GitHub Desktop.
function doPost(e) { | |
// get the parameters | |
var params = e.parameter | |
// run if command sends request | |
if (params.call == "command") { | |
// create payload | |
var trigger_id = params.trigger_id | |
var modal_payload = { | |
"trigger_id": trigger_id, | |
// create view itself | |
"view": { | |
"type": "modal", | |
"callback_id": "shopping_modal", | |
"title": { | |
"type": "plain_text", | |
"text": "Shopping Request" | |
}, | |
"submit": { | |
"type": "plain_text", | |
"text": "Submit" | |
}, | |
"close": { | |
"type": "plain_text", | |
"text": "Cancel" | |
}, | |
"blocks": [ | |
// build category block | |
{ | |
"type": "input", | |
"block_id": "category", | |
"label": { | |
"type": "plain_text", | |
"text": "What to buy?" | |
}, | |
"optional": false, | |
"element": { | |
"action_id": "single_select", | |
"type": "static_select", | |
"placeholder": { | |
"type": "plain_text", | |
"text": "Choose a category" | |
}, | |
"options": [ | |
{ | |
"value": "food", | |
"text": { | |
"type": "plain_text", | |
"text": "Food" | |
} | |
}, | |
{ | |
"value": "stationary", | |
"text": { | |
"type": "plain_text", | |
"text": "Stationary" | |
} | |
}, | |
{ | |
"value": "other", | |
"text": { | |
"type": "plain_text", | |
"text": "Other" | |
} | |
} | |
] | |
} | |
}, | |
// build items block | |
{ | |
"type": "input", | |
"block_id": "items", | |
"label": { | |
"type": "plain_text", | |
"text": "What items?" | |
}, | |
"optional": false, | |
"element": { | |
"action_id": "comment_box", | |
"type": "plain_text_input", | |
"multiline": true, | |
"placeholder": { | |
"type": "plain_text", | |
"text": "List what you need" | |
} | |
} | |
}, | |
// build date block | |
{ | |
"type": "input", | |
"block_id": "date", | |
"label": { | |
"type": "plain_text", | |
"text": "By when do you need it?" | |
}, | |
"optional": true, | |
"element": { | |
"action_id": "date_picker", | |
"type": "datepicker" | |
} | |
}, | |
// build comments block | |
{ | |
"type": "input", | |
"block_id": "comment", | |
"label": { | |
"type": "plain_text", | |
"text": "Any comments?" | |
}, | |
"optional": true, | |
"element": { | |
"action_id": "comment_box", | |
"type": "plain_text_input", | |
"multiline": true, | |
"placeholder": { | |
"type": "plain_text", | |
"text": "Provide extra details, if necessary" | |
} | |
} | |
} | |
] | |
} | |
} | |
// initiate modal in Slack | |
sendToSlack("https://slack.com/api/views.open", modal_payload) | |
} | |
// run when interaction sends request | |
else if (params.call == "interaction") { | |
var payload = JSON.parse(params.payload) | |
// run when sent modal and the right modal | |
if (payload.type == "view_submission" && payload.view.callback_id == "shopping_modal") { | |
// send results to #shopping app channel | |
var user_id = payload.user.id | |
var user_name = getUserName(user_id) | |
var date = new Date() | |
var date_string = Utilities.formatDate(date, "GMT", "yyyy-MM-dd") | |
var date_unix = Math.floor((date.getTime()/1000)).toString() | |
var view_vls = payload.view.state.values | |
var category = view_vls.category.single_select.selected_option.text.text | |
var items = view_vls.items.comment_box.value | |
var deadline = view_vls.date.date_picker.selected_date || "NA" | |
var comment = view_vls.comment.comment_box.value || "NA" | |
var ticket_info = { | |
"user_id": user_id, | |
"user_name": user_name, | |
"date": date_string, | |
"category": category, | |
"items": items, | |
"deadline": deadline, | |
"comment": comment | |
} | |
var ticket_info_str = JSON.stringify(ticket_info) | |
var result = { | |
"text": "A new request!", | |
"blocks": [ | |
{ | |
"type": "section", | |
"block_id": "text", | |
"text": { | |
"type": "mrkdwn", | |
"text": "You have a *new request*! :eyes:" | |
} | |
} | |
], | |
"attachments": [ | |
{ | |
"color": "#2469EC", | |
"fallback": "Shopping request", | |
"blocks": [ | |
{ | |
"type": "section", | |
"block_id": "category_items", | |
"fields": [ | |
{ | |
"type": "mrkdwn", | |
"text": "*Category*\n" + category | |
}, | |
{ | |
"type": "mrkdwn", | |
"text": "*Items*\n" + items | |
} | |
] | |
}, | |
{ | |
"type": "section", | |
"block_id": "deadline_comment", | |
"fields": [ | |
{ | |
"type": "mrkdwn", | |
"text": "*Deadline*\n" + deadline | |
}, | |
{ | |
"type": "mrkdwn", | |
"text": "*Comment*\n" + comment | |
} | |
] | |
}, | |
{ | |
"type": "context", | |
"block_id": "context", | |
"elements": [ | |
{ | |
"type": "mrkdwn", | |
"text": "Submitted by " + user_name + " | <!date^" + date_unix + "^{date_short_pretty} at {time}|" + date + ">" | |
} | |
] | |
} | |
] | |
}, | |
{ | |
"fallback": "Action buttons", | |
"blocks": [ | |
{ | |
"type": "actions", | |
"block_id": "action_buttons", | |
"elements": [ | |
{ | |
"type": "button", | |
"action_id": "approve_button", | |
"value": ticket_info_str, | |
"style": "primary", | |
"text": { | |
"type": "plain_text", | |
"text": "Approve" | |
} | |
}, | |
{ | |
"type": "button", | |
"action_id": "decline_button", | |
"value": ticket_info_str, | |
"style": "danger", | |
"text": { | |
"type": "plain_text", | |
"text": "Decline" | |
} | |
} | |
] | |
} | |
] | |
} | |
] | |
} | |
// send ticket to #shopping | |
sendToSlack("https://hooks.slack.com/services/XXXXXXXXX/XXXXXXXXXXX/xxxxxxxxxxxxxxxxxxxxxxxx", result) | |
// send notification to user | |
var msg = { | |
"channel": user_id, | |
"text": "Your shopping request was submitted.", | |
"blocks": [ | |
{ | |
"type": "section", | |
"text": { | |
"type": "mrkdwn", | |
"text": "Your shopping request was *submitted* to <#CXXXXXXXXXX> channel. :pencil:" | |
} | |
} | |
] | |
} | |
sendToSlack("https://slack.com/api/chat.postMessage", msg) | |
} | |
// run when interaction with buttons happened | |
else if (payload.type == "block_actions") { | |
var response_user_id = payload.user.id | |
var response_user_name = getUserName(response_user_id) | |
var channel_id = payload.channel.id | |
var response_url = payload.response_url | |
var response_time = new Date() | |
var response_time_unix = Math.floor((response_time.getTime()/1000)).toString() | |
var ticket_blocks = payload.message.attachments[0].blocks | |
var ticket_ts = payload.message.ts | |
var action_id = payload.actions[0].action_id | |
// get ticket values | |
var ticket_info = JSON.parse(payload.actions[0].value) | |
var user_id = ticket_info.user_id | |
var user_name = getUserName(user_id) | |
var date = ticket_info.date | |
var category = ticket_info.category | |
var items = ticket_info.items | |
var deadline = ticket_info.deadline | |
var comment = ticket_info.comment | |
// get ticket permalink | |
var ticket_origin = { | |
"channel": channel_id, | |
"message_ts": ticket_ts | |
} | |
var ticket_permalink = getMsgUrl(ticket_origin) | |
// run when approved | |
if (action_id == "approve_button") { | |
// send info to sheet | |
var ticket_data = [user_name, date, category, items, deadline, comment] | |
var ss_id = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" | |
sendToSheet(ss_id, ticket_data) | |
// prepare result and message | |
var result = { | |
"channel": user_id, | |
"blocks": [ | |
{ | |
"type": "section", | |
"block_id": "text", | |
"text": { | |
"type": "mrkdwn", | |
"text": "This request was *approved*. :white_check_mark:" | |
} | |
} | |
], | |
"attachments": [ | |
{ | |
"color": "#36A54F", | |
"blocks": ticket_blocks | |
}, | |
{ | |
"blocks": [ | |
{ | |
"type": "context", | |
"block_id": "status", | |
"elements": [ | |
{ | |
"type": "mrkdwn", | |
"text": "Approved by " + response_user_name + " | <!date^" + response_time_unix + "^{date_short_pretty} at {time}|" + response_time + ">" | |
} | |
] | |
} | |
] | |
} | |
] | |
} | |
var msg = { | |
"channel": user_id, | |
"text": "Your shopping request was approved.", | |
"unfurl_links": true, | |
"blocks": [ | |
{ | |
"type": "section", | |
"text": { | |
"type": "mrkdwn", | |
"text": "<" + ticket_permalink + "|Your shopping request> was *approved*. :white_check_mark:" | |
} | |
} | |
] | |
} | |
} | |
// run when declined | |
else if (action_id == "decline_button") { | |
// prepare result and message | |
var result = { | |
"channel": response_user_id, | |
"blocks": [ | |
{ | |
"type": "section", | |
"block_id": "text", | |
"text": { | |
"type": "mrkdwn", | |
"text": "This request was *declined*. :x:" | |
} | |
} | |
], | |
"attachments": [ | |
{ | |
"color": "#E83436", | |
"blocks": ticket_blocks | |
}, | |
{ | |
"blocks": [ | |
{ | |
"type": "context", | |
"block_id": "status", | |
"elements": [ | |
{ | |
"type": "mrkdwn", | |
"text": "Declined by " + response_user_name + " | <!date^" + response_time_unix + "^{date_short_pretty} at {time}|" + response_time + ">" | |
} | |
] | |
} | |
] | |
} | |
] | |
} | |
var msg = { | |
"channel": response_user_id, | |
"text": "Your shopping request was declined.", | |
"unfurl_links": true, | |
"blocks": [ | |
{ | |
"type": "section", | |
"text": { | |
"type": "mrkdwn", | |
"text": "<" + ticket_permalink + "|Your shopping request> was *declined*. :x:" | |
} | |
} | |
] | |
} | |
} | |
// update the message with appropriate result | |
sendToSlack(response_url, result) | |
// send notification to the user | |
sendToSlack("https://slack.com/api/chat.postMessage", msg) | |
} | |
} | |
// respond with basic acknowledgment response | |
return ContentService.createTextOutput("") | |
} | |
// function that sends data to Slack | |
function sendToSlack(url, payload) { | |
var options = { | |
"method": "post", | |
"contentType": "application/json", | |
"payload": JSON.stringify(payload), | |
"headers": {"Authorization": "Bearer xoxb-xxxxxxxxxxxx-xxxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxx"} | |
} | |
return UrlFetchApp.fetch(url, options) | |
} | |
// function that gets user name from Slack | |
function getUserName(user_id) { | |
var options = { | |
"method": "get", | |
"contentType": "application/x-www-form-urlencoded", | |
"payload": { | |
"user": user_id | |
}, | |
"headers": {"Authorization": "Bearer xoxb-xxxxxxxxxxxx-xxxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxx"} | |
} | |
var resp = UrlFetchApp.fetch("https://slack.com/api/users.info", options) | |
return JSON.parse(resp).user.profile.display_name_normalized | |
} | |
// function that gets URL of message from Slack | |
function getMsgUrl(payload) { | |
var options = { | |
"method": "get", | |
"contentType": "application/x-www-form-urlencoded", | |
"payload": payload, | |
"headers": {"Authorization": "Bearer xoxb-xxxxxxxxxxxx-xxxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxx"} | |
} | |
var resp = UrlFetchApp.fetch("https://slack.com/api/chat.getPermalink", options) | |
return JSON.parse(resp).permalink | |
} | |
// function that writes data to Sheets | |
function sendToSheet(spreadsheet_id, data) { | |
var ss = SpreadsheetApp.openById(spreadsheet_id) | |
var sheet = ss.getSheetByName("Requests") | |
var last_row = sheet.getLastRow(); | |
return sheet.getRange(last_row + 1, 1, 1, data.length).setValues([data]) | |
} |
It's not sent there by Slack. I do a small trick where I add ?call=command
at the end of the web app's URL to distinguish that this particular call is being done by a Slack command. I do the same thing when I add ?call=interaction
to know that interaction buttons are sending the request.
I hope this clears it up! If you haven't done so already, I'd certainly recommend checking the accompanying Medium article where I describe the process step by step. 😉
Thanks, again. What would you recommend if I needed to carry out another function in response to a modal submission that would cause the Slack wait time to elapse? Do you known of any asynchronous ways to invoke it or would you use another method like creating another web app to receive a payload and handle that function or setting a time based trigger for that function to execute after the response?
Frankly, I don't have any experience with this as I've never needed it myself.
From the top of my head, I think you can only let Google Apps Script wait through Utilities.sleep(milliseconds);
for up to a few minutes. Then, the script timeouts and stops. Should you need the wait to be longer, I think you'll need to setup another script, probably with a time-based trigger as you mention.
Just an idea – maybe you could store the modal submissions with its timestamp in a Google Sheet. They, you'd set up a separate script that would run each hour (as that's the most often you can do, I believe) and let it run the action you desire once required amount of time passed from the submission timestamp.
It's hardly the best that can be done, but it might work for you. If not, I'd suggest posting a question to Stackoverflow.
Thank you for your help, I really appreciate it. I can now see logs and that the doPost is starting to execute. For the e.parameter, is call referring to a value in the JSON array as I don't see a call field in the array?