Automating your Slack Profile Picture

Make a program that changes your Slack profile picture based on the time of day.

sampoder@sampoder

Having a plain old profile picture is so 2020. Having a Slack Profile Picture that changes throughout the day is a great way to mix things up on Slack. In this workshop, we'll be learning how to make a dynamic profile picture that changes as the day progresses. We'll be using Node.js deployed as a Vercel Serverless Function and triggered by cron-job.org. You'll be learning the basics of getting a Slack User Token, creating a Node.js program and deploying that program as a serverless function.

Obtaining a Slack Token

In my opinion this is the most painful part of this process.

To begin you will need to be a member of a Slack Workspace. The easiest way to join one is to join Hack Club's.

Then visit the Slack "Build" page. Click the large green button that says Create an App and select From scratch on the next screen.

Name it whatever you'd like, and choose the workspace you'd like the app to be for.

On the next screen, find the Add features and functionality section and select Permissions.

Slack API Configuration Screen with an arrow pointing to permissions

On this screen, scroll down to the User Token Scopes section and select the button that says Add an OAuth Scope.

OAuth Scope Selection on Slack API page

In the pop-up box that follows, select users.profile:write and add that scope.

OAuth Scope Selection on Slack API page completed

Scroll to the top of the page, click Install to Workspace, then Allow and then you will receive a token in return.

Sample Token from Slack

Copy the generated token and store it in a safe space. Don't share it with anyone else (this is a fake token in the image, don't worry).

Building our program

To get started you are going to want to create a new Node.js repl.it project, click here to do so.

Let's get going by adding your Slack token to your environment variables. What are environment variables? These are super secret variables that you don't want to store publicly.

Repl.it provides us an easy way to pass in these environment variables to our app, head over to the "Secrets" tab in the left sidebar: Secrets tab in repl.it

and inside the "key" type SLACK_TOKEN and inside the "value" section, paste in your Slack OAuth token and click on Add new secret button:

adding the secret in repl.it

Now, let's add two packages (axios & @slack/web-api) to our index.js file.

const { WebClient } = require('@slack/web-api');
const axios = require('axios').default;

Axios will be used to fetch the images from URLs and the Slack Web API package will be used to interact with Slack.

Next, let's create a data object with each of our images. Replace each URL with your own to make this program your own.

const images = {
  "morning": "https://images.unsplash.com/photo-1517188206596-1e1f7c954177?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1650&q=80",
  "afternoon": "https://images.unsplash.com/photo-1566452348683-91f934cd9051?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2090&q=80",
  "night": "https://images.unsplash.com/photo-1519446251021-1c4ee77fec1e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=913&q=80"
}

Setting our profile picture

Now that we've got our images we need to create an async function that will handle setting our profile picture.

async function setPFP() {  
  console.log(images)
}

An async function is important as it allows us to wait for a line of code to complete before moving to the next line using the await keyword.

What should we do first? How about setting a profile picture! Inside our setPFP function add the following lines. This will fetch the contents of the image.

const image = await axios.get(images.afternoon, {
  responseType: "arraybuffer"
});

Awesome! We now have the image, let's set it to our profile picture on Slack. We can do this using the users.setPhoto API endpoint.

const client = new  WebClient();
const slackRequest = await client.users.setPhoto({
  image: image.data,
  token: process.env.SLACK_TOKEN
});

In the above code we are creating an instance of the Slack Web API Client. Then we make a request to the users.setPhoto API endpoint, with our image data and our token. We need the token so that Slack knows who we are and that we are allowed to make this change. We're sending the data as an array buffer, these are a bit complicated but if you're curious you can read more about them here.

Now to run this function, just add the following to the bottom of you index.js file:

setPFP()

This simply calls your function, now click Run ►.

Check Slack... do you see any difference in your profile? Fingers crossed you do but if you don't, check the console for any errors and check your code for any typos ;)

Changing the Profile Picture Based on Time

Sooooo it may be the afternoon for you but it may not ¯\_(ツ)_/¯

Try changing images.afternoon to images.morning and running your program. You should see a different image as your profile picture after a couple of seconds.

To do so we'll need to know how many hours have passed in the day, right?

var hour = new Date().getHours()

Add this code to your program at the start of your function, it creates a variable and assigns it the value of how many hours have passed. If you add console.log(hour) you'll notice that these hours aren't in your timezone (unless you are in an exact UTC zone). This is because these times are in UTC. I'm not going to go on and on about timezone nonsense but you'll need to add or remove the UTC offset for your region.

For example, I live in Singapore so my timezone is UTC+8 and has an offset of +8 so I would write:

var hour = new Date().getHours() + 8

Now let's set the image based on hours using a couple of if statements and an else statement:

let image
if (5 < hour && hour < 12) {
  image = await axios.get(images.morning, {
    responseType: "arraybuffer",
  });
}
else if (12 < hour && hour < 20) {
  image = await axios.get(images.afternoon, {
    responseType: "arraybuffer",
  });
}
else {
  image = await axios.get(images.night, {
    responseType: "arraybuffer",
  });
}

We want to replace our original image fetching code block with this to make our final code file this:

const { WebClient } = require('@slack/web-api');
const axios = require('axios').default;
const images = {
  "morning": "https://images.unsplash.com/photo-1517188206596-1e1f7c954177?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1650&q=80",
  "afternoon": "https://images.unsplash.com/photo-1566452348683-91f934cd9051?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2090&q=80",
  "night": "https://images.unsplash.com/photo-1519446251021-1c4ee77fec1e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=913&q=80"
} 
async function setPFP() {
  var hour = new Date().getHours() + 8
  let image
  if (5 < hour && hour < 12) {
    image = await axios.get(images.morning, {
      responseType: "arraybuffer",
    });
  }
  else if (12 < hour && hour < 20) {
    image = await axios.get(images.afternoon, {
      responseType: "arraybuffer",
    });
  }
  else {
    image = await axios.get(images.night, {
      responseType: "arraybuffer",
    });
  }
  const client = new WebClient();
  const slackRequest = await client.users.setPhoto({
    image: image.data,
    token: process.env.SLACK_TOKEN
  });
}

setPFP()

Making it serverless on Vercel

Serverless is basically a buzz word by hosting companies that means running code server side, without managing your own server. Yep, it's not really serverless.

Anyhow, we'll need to move our code inside of an api folder. So create a new folder called api , create a index.js file in there and copy all of your code into it.

Now we'll need to replace setPFP() with:

export default async (req, res) => {
  await setPFP()
  res.send("Started changing your PFP!")
}

That's all the code changes we'll need. Now we need to deploy it!

First, we'll add it to GitHub (a code hosting platform). If you don't have an account please register one here and then come back :)

Click the Version Control button:

Version Control Icon on Repl.it

Then click the Create a Git Repo button

Create a Git Repo Button

Then click Connect to GitHub. The following process is different for different people so please follow the onscreen instructions.

Connect to GitHub Button

Once you get to this screen, copy this link.

Link to copy with arrow

Now head to vercel.com and register for an account.

Once you have registered for Vercel, visit https://vercel.com/import and click this button:

Vercel Git Repo Button

Paste in the link you previously copied and click continue. Click continue again and then deploy!

Congrats page

If all goes to plan you should see this page, yay!! We have deployed it. Now we need to configure our environment variables. Open the dashboard up.

Click to open dashboard

Then open up the settings:

Open settings

Create an environment secret for your Slack token with the following settings:

Add env variable

Now we need to redeploy our app. You can do this by going to Deployments:

Deployments button

Then select the top one:

Selecting the top one

Then in the top right, click the three dots and then the redeploy button.

Redeploy button location

Now try visiting /api on your url. You should see your profile picture change on Slack ;) Check it out! But... we don't want to have to visit this URL every time. So, let's automate that.

Scheduling a trigger

Now let's schedule when to trigger this API. Head on over to cron-job.org and create a new account.

Once you've logged into that account, click Cronjobs.

Cronjobs button

Then create a new one!

Create new button

Fill in the form with the following settings and click create:

Recommended Settings

And wow... we're done!! Now throughout the day your profile picture will change depending on the time.

Hacking

Slack profile pictures are semi-unexplored territory but there's certainly a lot of hacking potential in them! So go make something EPIC!

Here are a couple of examples of cool Slack profile picture hacks:

Make something cool? Awesomeeee!!!! Share it on #ship in the Slack and tag me with @sampoder!

Edit this page on GitHub