Build a free Slack App using Google Apps Scripts and some fun features to make you look cool to your coworkers
By: Matt, Rudy
Tags: Ron Swanson, Xkcd, Slack, Google Apps Script, Slack Apps

Build a free Slack App using Google Apps Scripts and some fun features to make you look cool to your coworkers Matt “Rudy” Benton Jan 22·14 min read

Custom Slack Apps can be cool

I have rewritten this multiple times trying to come up with a catchy intro for this blogpost. At this point, I am going to stop attempting and inform you that this is now one of the many posts out there about creating a Slack App and how a Slack integration can be used to improve things in an organization. This post should give you insight on how to set up a Slack App utilizing Google Apps Script at no additional costs. Additionally, I will share some fun features I added to my App that may give you new ideas of what could be done with this simple setup.

So how did this all come about: I was part of a small team that put together and executed a virtual week of holiday festivities for Maveris in lieu of an in-person company holiday party. A simple explanation of the week was that there were daily competitions for employees to compete in through submitting and voting in Google Forms as well as meetups and other random banter in created Slack Channels. An underlying goal of the competitions and events was to win “Magic Maveris Medallions’’ (to be more concise, I’ll refer to this as points from now on in my writing), that were essentially used as raffle tickets for prize drawings at the end of the week. With people working on different tasks and supporting different contracts, the team decided to attempt to use Slack as a central communication point for employees related to the festivities. As you will see later, it also provided some additional functionality to improve events or aid in solutions to possible problems we discovered while working with the Google Suite. We did not get everything integrated into Slack as we had initially hoped, due to various constraints, however, we still feel it worked out great. I just hope if you read through the blog it gives you ideas and with your imagination, how you can improve things in your organization at no additional cost.

Before we start, in case you are interested in more details about the week of festivities, check out post on the company blog: https://maveris.com/maveristmas/

Additionally, some other posts related to the technical aspects of Maversitmas that may be helpful to others:

Disclaimer: In this blogpost I am going to assume the reader has some familiarity with Slack and how it works. I also may assume that the reader is familiar with some Google Apps Script stuff. If you are not familiar with it and your company or you personally use google, check it out. Thanks to my friend @dillenbl for telling me about all the cool things he has been doing with it and opening my eyes to it.

As I mentioned above, employees essentially won raffle tickets. And if you are competitive like I am, you will want to know how many of these tickets or points you have at any time. I personally didn’t want to field messages all day of people asking if I could tell them how many points they had. That with the fact I had wanted to create a little Slack App from the beginning, I thought, “What a great idea. Can I build a Slack App so that the employee can just make a request and get their own response?”

The Team was already tracking each person’s point totals in a Google Sheet (mentioned in more detail in the referenced blog posts above) tied to each person’s email address. Since Slack for our company can only be used with your Maveris email account, I felt this could be a simple solution.

Create a Slack App in the Maveris Slack Workspace Create a Google Apps Script, deploy it as web application, receive the request from Slack Create a slash command for the Slack App, configured to send requests to the deployed web application Parse the web request from Slack and retrieve the info Return the info to the Slack user

Create a Slack App in the Maveris Slack Workspace

There is already enough info out there on how to create a Slack App. Just google this and follow the steps to create a Slack App for the workspace. Now just like when you create your own Rock band, the name is the most important part. So ensure you come up with a cool name for your Slack Bot.

Pick an Awesome name

Create a Google Apps Script, deploy it as web application, receive the request from Slack

First, the advantage to using Google Apps Script is that it is free, plus in our case it is tied into our design for these events. We already decided to use Google Forms and Google Sheets, so it was built into that infrastructure. From Google Apps Script, you can easily access other Google Sheets, files in Google Drive, values in Google Sheets, etc. Basically you can build and deploy a small web app without worrying about a lot of the infrastructure. You are bound to some of the ways Google may do things, but again like I said, it’s free and with some trial and errors or sweat and tears you can get it to work.

So, create a project at scripts.google.com. The big thing to know is that to publish any script as a web app they must meet a few requirements:

A script can be published as a web app if it meets these requirements (https://developers.google.com/apps-script/guides/web):

It contains a doGet(e) or doPost(e) function.

or function. The function returns an HTML service HtmlOutput object or a Content service TextOutput object.

Make note of the e, this is an event object that is tied to an event in Apps Script. So in a doPost(e) function, the e is a reference event from a POST request. This is where your Slack App will make its POST request with information. So create a function below just to get started (you will change this later):

function doPost(e) {

var a = “Hello Maveris Labs”;

return HtmlService.createHtmlOutput(a);

}

Deploy the script as a Web Application (Publish -> Deploy as Web App..) and get the URL for the web app. Make sure you have the Web App set to execute the app as yourself and the access as “Anyone, even anonymous”

Create a slash command for the Slack App, configured to send requests to the deployed web application

So this part gets a little confusing, or it did for me. Seems there are all types of custom Slack integrations; ways to manage Slack Apps in a workspace. To create the slash command for the App, you want to access it through api.slack.com/apps. I, at times, have spent time trying to find this and getting stuff in the Slack Workspace management of Apps that doesn’t allow you to modify the App itself. This is the same place you can also add incoming webhooks for your App and do way more things then I yet know how to even do. I hope you don’t get confused as I did.

So once in the Slack Api section and you have selected the App you have permissions to adjust, select the Slash Commands. This works by having the App as part of the workspace and adding the slash command allows any user to run the /command which then has the App run by a created bot-user. As you can see in the image below, I have my App called Maveristmas-Bot and I created a Slash Command /maveristmas that invokes the App to send data to the URL.

Put the URL for your deployed web app from above in the Request URL section. After this is complete, when a user runs the desired slash command /command, a POST request is made to the deployed web app and the doPost(e) function is executed as mentioned before.

For my design of the App, I chose to keep it simple and just make one slash command and had the information sent after it as the commands. This way I didn’t have to create new slash commands when I added new pointless features. All I had to do was update the Google Apps Script and redeploy the Web App with no changes to Slack. This is up to the reader how they want to do it, but worked well for me.

To aid in troubleshooting or development:

During development or troubleshooting it may be difficult to ensure your data in the event object e, is what you expected. There are different ways to accomplish this and each developer has their way they desire to do so. I stole some code from a StackOverflow post (regrettably, I don’t have the reference at this time to provide credit, and added it to the doPost(e) function):

var spreadsheet_id = "SHEET-ID-GOES-HERE";

var ss = SpreadsheetApp.openById(spreadsheet_id);

var sheet = ss.getSheetByName('log');

sheet.getRange(1,1).setValue(JSON.stringify(e));

This snippet of code will add the current event object to the provided Google Sheet (ensure the script has the correct permissions and scope to access the Sheet). Each time doPost is executed, the script will overwrite the data in the first cell and show the last event object. It is definitely not pretty but it works. Obviously with some slight modification to the snippet you could have it log all event objects per line or maybe just the parameters from the event object, etc.

For example, while developing the Slack App, I was unsure exactly how the POST data for a slash command (this link would have helped back then) was sent to my deployed Apps Script.

Executing the /maveristmas apple command in Slack and using the above code snippet provided me the ability to see what data was in the event object to work with. Below is a beautified version of the data provided in the spreadsheet:

Beautified event object of the data from a slash command in Slack

From this image we can determine that Slack sends data in JSON format. In the data, the user_name and the text fields are important to me. The user_name field will be important to determine who made the request and the text field is the data after the slash command. Remember this is how I chose to build the App, however, you could choose to separate slash commands and then parse on the command field.

Parse the web request from Slack and retrieve the info

So now you have a way for your users to make a request that sends a post request and data to your App script. You can see above how the data gets in. You simply parse the request, do whatever magic you want with it and send it back to Slack. Then, as long as it goes back formatted correctly, it will be displayed to the user privately.

In the doPost function, I put the code in an if statement that only processed if the event had data. Then I used Google’s built-in methods for the event parameter (https://developers.google.com/apps-script/guides/web). I parse out the parameters for the user_name, command, and the text. I decided I didn’t care if there was extra stuff sent by the user and just based everything off whatever the first word was after the slash command.

if (e !== 'undefined') {

var slack_params = e.parameter;

var userName = slack_params.user_name;

var slack_cmd = slack_params.command;

var slack_cmd_params = slack_params.text;

var slack_first_param = slack_cmd_params.split(" ", 1);

Now that the data is parsed, I need to get the data I want. As I mentioned before I wanted to allow the employees to check on their point total. So I created another if statement that checked for the slack_first_param == mytotal and if it was, then I looked up the username in the Team’s Google Sheet that was tracking all the points and returned the value from the total column. The lookup function, stolen from my co-worker @thecyberbutler with way more javascript-Fu then I, can be found on his post here or a gist here.

The returned value was then put into a message variable then returned to Slack return ContentService.createTextOutput(JSON.stringify({“text” : message,})).setMimeType(ContentService.MimeType.JSON);

As I mentioned before there are all types of fancy ways to send messages back and format them, with pictures and other cool things. But for simplicity sake, I initially did this and just added some emojis to look cool.

Example of the Slack App response for checking on points (MMMs represents the points)

So here is the very basic format of code for the Slack App in some of my original design. I hope it helps give you a nice setup for things you can do and improve on it.

function doPost(e) {

//code snippit to just log POST requests for testing

var spreadsheet_id = "ADD-SHEET-ID-HERE";

var ss = SpreadsheetApp.openById(spreadsheet_id);

var sheet = ss.getSheetByName('log');

sheet.getRange(1,1).setValue(JSON.stringify(e)); if (e !== 'undefined') {

var slack_params = e.parameter;

var userName = slack_params.user_name;

var slack_cmd = slack_params.command;

var slack_cmd_params = slack_params.text;

var slack_first_param = slack_cmd_params.split(" ", 1); if (slack_first_param.toString().toLowerCase() == "mytotal") {

message = ":boom: You currently have " + lookupValue(userName) + " MMMs!!:star2:";

return ContentService.createTextOutput(JSON.stringify({"text" : message,})).setMimeType(ContentService.MimeType.JSON);

}

}

}

Improving the Slack App with more features

So after creating the initial Slack App, I had to add a few pointless functions and make it way more fun.

THAT’S HOW I ROLL

From the above code, I changed out the If statement and just created a switch statement. This made it easier to add things to the script. The script would check the first parameter passed and then work through the switch statement looking for a match. Once the match was found, a separate function could be called to return the desired message to send to the Slack user. Then whatever I needed to have done I could take care of in that function, or multiple functions. This really made the script more modular and ability to adapt on the flow as needed. After the switch statement completes the message is then returned to the Slack user (you may notice in the xkcd function I had to format the message differently and did not follow this exact flow).

As you can see above I created some pointless functions. But I will say I received a lot of comments from employees and could be morale boosting for your organization. Now I did not create a whole new API with thousands of Ron Swanson jokes or Chuck Norris jokes. I did one better, I found someone else who is already providing this service for free. I then created a function that would use App Scripts UrlFetchApp.fetch() function to make a request to the API. Then I would parse the data for what I wanted from it and return it to the user. To the user, my bot was magically giving them some cool Chuck Norris jokes for them to laugh at.

Chuck Norris Joke:

/maveristmas chucknorris



var chuck_web_url = "

var response = UrlFetchApp.fetch(chuck_web_url);

var obj = JSON.parse(response);

var joke = obj["value"]["joke"];

return (joke);

} function get_chucknorris_joke(){var chuck_web_url = " http://api.icndb.com/jokes/random?exclude=[explicit ]"var response = UrlFetchApp.fetch(chuck_web_url);var obj = JSON.parse(response);var joke = obj["value"]["joke"];return (joke);

This API allows the user to exclude explicit jokes, being for work, I suggest you do that.

Ron Swanson Quote:

/maveristmas swanson



var swanson_web_url = "

var response = UrlFetchApp.fetch(swanson_web_url);

return response.getContentText().slice(1,-1) + "- Ron Swanson";

} function get_swanson(){var swanson_web_url = " https://ron-swanson-quotes.herokuapp.com/v2/quotes var response = UrlFetchApp.fetch(swanson_web_url);return response.getContentText().slice(1,-1) + "- Ron Swanson";

I decided that since it was quotes from Ron Swanson, I added the ending there to make it look more like a quote. I thought about working to add an image with it, maybe next time. And if you don’t know who Ron Swanson is, I strongly recommend googling him and learning from his vast knowledge.

Random Fact:

/maveristmas randomfact



var fact_web_url = "

var response = UrlFetchApp.fetch(fact_web_url);

var obj = JSON.parse(response);

var fact = obj[0]["headline"];

return fact;

} function get_randomfact (){var fact_web_url = " https://www.mentalfloss.com/api/facts ";var response = UrlFetchApp.fetch(fact_web_url);var obj = JSON.parse(response);var fact = obj[0]["headline"];return fact;

There was a lot of different info you could pull in from this API response. I kept it simple and just grabbed the headline as the fact.

XKCD Comic:

//for the xkcd function to get a random value

function getRandomArbitrary(min, max) {

return Math.random() * (max - min) + min;

} function create_image_block_payload(image_url, text ="Random text"){

var payload = {

"blocks": [

{

"type": "image",

"title": {

"type": "plain_text",

"text": text

},

"block_id": "image4",

"image_url": image_url,

"alt_text": text

}

]

};

return payload

}

//the 2150 value is not the highest number it can go, it increases each day. Large enough for me

var comic_number = Math.trunc(getRandomArbitrary(0,2150));

var xkcd_web_url = "

var response = UrlFetchApp.fetch(xkcd_web_url);

if (response.getResponseCode == 404){

var payload= {"text" : "Error trying to get an xkcd image, please try again",} ;

return payload

}

var obj = JSON.parse(response);

var comic_url = obj["img"];

var img_title = obj["safe_title"];

return create_image_block_payload(comic_url, img_title);

} function get_xkcd(){//the 2150 value is not the highest number it can go, it increases each day. Large enough for mevar comic_number = Math.trunc(getRandomArbitrary(0,2150));var xkcd_web_url = " https://xkcd.com/ " + comic_number + "/info.0.json";var response = UrlFetchApp.fetch(xkcd_web_url);if (response.getResponseCode == 404){var payload= {"text" : "Error trying to get an xkcd image, please try again",} ;return payloadvar obj = JSON.parse(response);var comic_url = obj["img"];var img_title = obj["safe_title"];return create_image_block_payload(comic_url, img_title);

For the xkcd function you can see it was a little more complicated. This was because I had built my switch statement to send back just simple strings as messages. In this case I had to return the message in a different format so Slack can present back an image. I am not going to dig into this here and say just check out Slack Block Kit Builder and see all the cool things you can do. Next time I want to do more with this to make everything look fancier.

Also, the xkcd API doesn’t really give a random response, so I had to create the randomization. My approach was to figure out about how many different comic numbers there were out there. At the time there were over 2150 and 0 is the latest comic number. I then just grabbed a random number and then made the random API request to that comic number. In the response it has the URL for the actual image. That is what then gets returned to the user in Slack. Then Slack will go pull down the image from the provided URL.

So there you go, if you have made it to the end of this blogpost, you now have an idea of how to create a simple Slack App, and then add some pointless but fun functionality. I hope this helps someone. If there are better easier ways to do this please let me know. Here is a gist of the full script for anyone that wants it or wants to tell me how to do things better.