Kevin Guebert

Kevin Guebert

☕️ Proudly built in Atlanta, GA

Stop Motion Videos...in Slack? Spice Up Your Profile Picture with Cron Jobs & Github Actions!

Monday, March 15, 2021

14 min read

Over the past year, many of us have been working from home with most of our communication happening on Slack. We can use our status to update our current situation whether it is in a heads down coding mode (do not disturb), out at lunch, at a doctors appointment, on vacation, not feeling well, or whatever emoji and text you are feeling.

The idea for this came from a simple question "what if people could know your status just by viewing your profile picture?"

The Idea

Update my profile picture in Slack throughout the day based on the time without being too disruptive.

A key part of this plan is that some people use other people's profile picture as a quick "who am I talking to" reference. Instead of reading names in the sidebar or the chat, they have become accustomed to one's profile picture. The images needed to update often enough to change "state" but not be too dramatic of changes to interrupt coworker's "flow."

Slack Stop Motion was born.

Slack Stop Motion

Implementation

From my initial idea and my requirements, I started brainstorming how to implement the idea. What I landed on was pretty simple:

  • A set of photos of me coming into the office and then a set of photos of me leaving the office
  • A cron job
  • Slack API
  • Github Action

Images

I've taken so many images with my webcam to try and figure this part out. I have a Logitech C920 that I thought would work but kept on running into small but frustrating issues. I used a terminal command to take pictures every 1 second to slowly move out of the camera, but the images there were quite grainy and low resolution. I tried using my Bluetooth mouse to click "capture" once I was in place, but those images ended up not being cohesive enough.

The last thing I did was put my iPhone on this $10 Amazon selfie stick that contains a Bluetooth button to trigger the capture. This solution worked out best in terms of quality and ease of use.

Cron Job

A cron job was the simplest solution and the one that immediately came to mind. I wanted to have the photos update on a fairly defined cadence and on certain days of the week (I am excluding weekends). Cron can do all that for you - and more!

Slack API

Integrating the Slack API took some messing around with but their documentation is easy to follow and click through to get what you need. The only things you need to get the Slack API working are:

  • A Slack App installed on your workspace
  • The users.profile:write role grant for the app

From there, it's just like calling an API!

Github Actions

I thought about looking into more defined solutions from Google, AWS, and Microsoft, but at the end of the day, remember this is just a simple cron job. I didn't want to over architect a cron job. I learned that Github Actions can do a simple cron that can run whenever you define it - perfect!

Tutorial

Below is the step-by-step guide of how to set up your instance. I also have a public Github repository of what I'm using now if you'd like to visit that as well.

Gather Images

The first thing to do is to grab all your photos and map out when you want to update your profile and to what picture. This can be a fairly intensive task as it depends on the number of photos you have, your work hours, how often you want to update, etc.

note

I am not including this in part of the tutorial. Take your own photos! It's fun to do and you can spice it up to however you like.

tip
  • You'll want to think about how many pictures you want to take and what time they would appear. The number of photos determines when the cron job should run.
    • If you want it to run on the hour every hour from 8:00AM to 5:00PM, you'll need about 10 photos.
    • If you want it to run every 30 minutes, you'll need about 20 photos.
    • You can do the math for your situation. (I had to break out the counting on my hand for this.)
  • Name your images what time you want them to show up. This will make it easier when we build out the script.

I created a folder on my Desktop, I named it images, and I added my images in there.

Images folder desktop

Here are all my images in gif form to help get an understanding of how I flowed them:

Slack Stop Motion

Create a Slack App

In this tutorial, we are only creating a one-off Slack app. If you want to look into how to create one that other people can sign in with, capture their tokens, be secure, and market, be my guest.

  1. Visit your Slack workspace, click on the workspace name in the top left and then go to Settings & Administation -> Manage apps

    Manage App

  2. From there, in the top right, click on "Build"

    Build

  3. Click "Create New App", give it a name, and add it to your Slack Workspace

    Create App
    Name App

  4. From here, on the left-hand side under "Features" click on "OAuth & Permissions"

    Oauth and Permissions

  5. Scroll down to "Scopes" and find "User Token Scopes"

  6. Click "Add an Oauth Scope" and search for users.profile:write

    Scopes

  7. Now scroll back up to the top and under the "Oath Tokens & Redirect URLs" click on "Install to Workspace"

    Scopes

  8. Allow the app to be installed on your workspace

    Install

  9. After installation, you will see the title "User Oauth Token" - this is very important - this is how we access the Slack API. Copy and save this to a safe location for now.

    Important Token

Creating the Script

  1. Now that we have our images and our Slack API key, we can go ahead and create our script locally to test it out. The script is really, really basic. Here's a brief overview:

    • Get's the current hour
    • Get's the current minute
    • A lot of switch and if statements.

    Super basic, I know.

  2. To create our script, let's create a file called slack-stop-motion.sh

  3. Open up the file in your favorite code editor and add we are going to add our code to get the current hour and the current minute:

    1#!/bin/bash
    2
    3hour=$(TZ=":America/New_York" date +%H)
    4minute=$(TZ=":America/New_York" date +%M)
  4. With those two values, we can do our math/configuration of "at what time we want certain photos to display:.

  5. I have 30 images and I want them to change every 20 minutes. So I did some sweet calculations, determined what image I wanted when and made those if statements work. All I'm doing is checking the hour and then checking what minute it currently is to determine if it is the 0th, 20th, or 40th minute. 🙃

    note

    Your images and timing will be different than mine! Probably very different. Unless you have the same image name and timing structure, this won't work. I also use America/New_York as my timezone.

    note

    I set my "default" image to be when I am "away".

    1#!/bin/bash
    2
    3hour=$(TZ=":America/New_York" date +%H)
    4minute=$(TZ=":America/New_York" date +%M)
    5
    6img="1740.jpg"
    7
    8case $hour in
    97)
    10if (($minute < 20)); then
    11 img="0700.jpg"
    12elif (($minute < 40)): then
    13 img="0720.jpg"
    14else
    15 img="0740.jpg"
    16fi
    17;;
    188)
    19if (($minute < 20)); then
    20 img="0800.jpg"
    21elif (($minute < 40)): then
    22 img="0820.jpg"
    23else
    24 img="0840.jpg"
    25fi
    26;;
    279)
    28if (($minute < 20)); then
    29 img="0900.jpg"
    30elif (($minute < 40)): then
    31 img="0920.jpg"
    32else
    33 img="0940.jpg"
    34fi
    35;;
    3610)
    37if (($minute < 20)); then
    38 img="1000.jpg"
    39elif (($minute < 40)): then
    40 img="1020.jpg"
    41else
    42 img="1040.jpg"
    43fi
    44;;
    4511)
    46if (($minute < 20)); then
    47 img="1100.jpg"
    48elif (($minute < 40)): then
    49 img="1120.jpg"
    50else
    51 img="1140.jpg"
    52fi
    53;;
    5412)
    55if (($minute < 20)); then
    56 img="1200.jpg"
    57elif (($minute < 40)): then
    58 img="1220.jpg"
    59else
    60 img="1240.jpg"
    61fi
    62;;
    6313)
    64if (($minute < 20)); then
    65 img="1300.jpg"
    66elif (($minute < 40)): then
    67 img="1320.jpg"
    68else
    69 img="1340.jpg"
    70fi
    71;;
    7214)
    73if (($minute < 20)); then
    74 img="1400.jpg"
    75elif (($minute < 40)): then
    76 img="1420.jpg"
    77else
    78 img="1440.jpg"
    79fi
    80;;
    8115)
    82if (($minute < 20)); then
    83 img="1500.jpg"
    84elif (($minute < 40)): then
    85 img="1520.jpg"
    86else
    87 img="1540.jpg"
    88fi
    89;;
    9016)
    91if (($minute < 20)); then
    92 img="1600.jpg"
    93elif (($minute < 40)): then
    94 img="1620.jpg"
    95else
    96 img="1640.jpg"
    97fi
    98;;
    9917)
    100if (($minute < 20)); then
    101 img="1700.jpg"
    102elif (($minute < 40)): then
    103 img="1720.jpg"
    104else
    105 img="1740.jpg"
    106fi
    107;;
    108esac
  6. Could this be more efficient? Oh you betcha. But that's for another time.

  7. Let's make sure we set the current image path based on our folder structure:

    1<!-- ...previous code -->
    2
    3img="./images/${img}"
  8. Finally, we can now send our request to Slack! We will be making a simple curl POST request.

    1<!-- ...previous code -->
    2
    3img="./images/${img}"
    4
    5curl --location --request POST 'https://slack.com/api/users.setPhoto' --form "token=xoxp-XYZ" --form "image=@${img}"
    note

    Be sure to replace xoxp-XYZ with your actual Slack API token from before!

  9. Return to your terminal and we can now safely run the script:

    $sh ./slack-stop-motion.sh
  10. Hopefully there is a successful response like below. If not, tweet at me and I will try and help you out!

    1{
    2 "ok": true,
    3 "profile": {
    4 "image_24": "https://avatars.slack-edge.com/2021-03-24/1919928028576_d0f5e53078c3e08fbcd3_24.jpg",
    5 "image_32": "https://avatars.slack-edge.com/2021-03-24/1919928028576_d0f5e53078c3e08fbcd3_32.jpg",
    6 "image_48": "https://avatars.slack-edge.com/2021-03-24/1919928028576_d0f5e53078c3e08fbcd3_48.jpg",
    7 "image_72": "https://avatars.slack-edge.com/2021-03-24/1919928028576_d0f5e53078c3e08fbcd3_72.jpg",
    8 "image_192": "https://avatars.slack-edge.com/2021-03-24/1919928028576_d0f5e53078c3e08fbcd3_192.jpg",
    9 "image_512": "https://avatars.slack-edge.com/2021-03-24/1919928028576_d0f5e53078c3e08fbcd3_512.jpg",
    10 "image_1024": "https://avatars.slack-edge.com/2021-03-24/1919928028576_d0f5e53078c3e08fbcd3_1024.jpg",
    11 "image_original": "https://avatars.slack-edge.com/2021-03-24/1919928028576_d0f5e53078c3e08fbcd3_original.jpg",
    12 "avatar_hash": "d0f5e53078c3"
    13 }
    14}
    note

    Depending on the time of day, the image that shows up may not be what you "expect". Be sure to double-check!

  11. Here's our full script that we will then use later on:

    1#!/bin/bash
    2
    3hour=$(TZ=":America/New_York" date +%H)
    4minute=$(TZ=":America/New_York" date +%M)
    5
    6img="1740.jpg"
    7
    8case $hour in
    97)
    10if (($minute < 20)); then
    11 img="0700.jpg"
    12elif (($minute < 40)): then
    13 img="0720.jpg"
    14else
    15 img="0740.jpg"
    16fi
    17;;
    188)
    19if (($minute < 20)); then
    20 img="0800.jpg"
    21elif (($minute < 40)): then
    22 img="0820.jpg"
    23else
    24 img="0840.jpg"
    25fi
    26;;
    279)
    28if (($minute < 20)); then
    29 img="0900.jpg"
    30elif (($minute < 40)): then
    31 img="0920.jpg"
    32else
    33 img="0940.jpg"
    34fi
    35;;
    3610)
    37if (($minute < 20)); then
    38 img="1000.jpg"
    39elif (($minute < 40)): then
    40 img="1020.jpg"
    41else
    42 img="1040.jpg"
    43fi
    44;;
    4511)
    46if (($minute < 20)); then
    47 img="1100.jpg"
    48elif (($minute < 40)): then
    49 img="1120.jpg"
    50else
    51 img="1140.jpg"
    52fi
    53;;
    5412)
    55if (($minute < 20)); then
    56 img="1200.jpg"
    57elif (($minute < 40)): then
    58 img="1220.jpg"
    59else
    60 img="1240.jpg"
    61fi
    62;;
    6313)
    64if (($minute < 20)); then
    65 img="1300.jpg"
    66elif (($minute < 40)): then
    67 img="1320.jpg"
    68else
    69 img="1340.jpg"
    70fi
    71;;
    7214)
    73if (($minute < 20)); then
    74 img="1400.jpg"
    75elif (($minute < 40)): then
    76 img="1420.jpg"
    77else
    78 img="1440.jpg"
    79fi
    80;;
    8115)
    82if (($minute < 20)); then
    83 img="1500.jpg"
    84elif (($minute < 40)): then
    85 img="1520.jpg"
    86else
    87 img="1540.jpg"
    88fi
    89;;
    9016)
    91if (($minute < 20)); then
    92 img="1600.jpg"
    93elif (($minute < 40)): then
    94 img="1620.jpg"
    95else
    96 img="1640.jpg"
    97fi
    98;;
    9917)
    100if (($minute < 20)); then
    101 img="1700.jpg"
    102elif (($minute < 40)): then
    103 img="1720.jpg"
    104else
    105 img="1740.jpg"
    106fi
    107;;
    108esac
    109img="./images/${img}"
    110
    111curl --location --request POST 'https://slack.com/api/users.setPhoto' --form "token=xoxp-XYZ" --form "image=@${img}"

Setting up Github Repository

  1. Be sure you have a Github Account;
  2. Next, create a new repository. I named mine slack-stop-motion but you can name yours whatever you like. Click the green "Create" button at the bottom of the page.

Create New Repository

  1. On the next page, there will be a bunch of information about adding files to your Github repository. Under the "Quick Setup" headline, there is a link that says "uploading an existing file". This is how we will add our images.

Upload Existing File

  1. It will probably be easier to drag and drop your whole folder in on this UI to add your images to the repository.

Add Files

  1. Make sure to scroll to the bottom to commit your changes!

Commit Images

  1. Once they are finished uploading, you should be taken back to your repository homepage with your images folder.

Repository Homepage

  1. The last thing we are going to do is to add our slack-stop-motion.sh script to the repository as well.
warning

Edit your slack-stop-motion.sh file and remove your key from the file and replace it with ${TOKEN}. The curl command should look like the following:

1curl --location --request POST 'https://slack.com/api/users.setPhoto' --form "token=${TOKEN}" --form "image=@${img}"
  1. Next, go through the same process of uploading your file like you did with the images.

Upload Individual File
Repo with Script

Adding Your Slack Key

  1. In order to safely and securely use the Slack API, we need to add our Slack API key to our repository. Thankfully, Github has thought of this problem and solved it for us!

  2. Click on "Settings" and then click on "Secrets" in the sidebar.

Settings & Secret

  1. Click "New Repository Secret"

New Repository Secret

  1. Create a secret with the name: "TOKEN" and paste in your Slack API Key from above. (Hint, it should start with xoxp if you followed the steps correctly.)

Slack API Token

Creating Your Github Action

  1. Our Github Action will be the foundation of our project. To get started, click on "Actions". (Don't see Github Actions? Visit the Documentation to learn more about how to get started.)

Github Actions

  1. Click "set up a workflow yourself"

Set up a workflow yourself

  1. Now we are onto some coding! This is where some of your custom configuration may come in; I run my cron job every 20 minutes between the hours of 7AM and 5PM. Here is the start of what my workflow looks like:

    1name: Slack Stop Motion
    2on:
    3 schedule:
    4 - cron: "*/20 11-21 * * mon-fri"
    note

    Cron can do some marvelous things. Visit https://crontab.guru to schedule to your heart's desire!

    Also note - check the timezone for cron on Github! It isn't Eastern 😬 that's why the cron is from 11-21 to account for the time difference.

  2. The next part of our workflow is calling the script we made earlier. There are a couple steps here that we want to make sure to hit:

    • Make sure the system has permissions to run the script (chmod)
    • Make sure our TOKEN from our environment secrets is defined in the environment
    • Call our script with the token.
    note

    Make sure the slack-stop-motion.sh script doesn't contain your actual Slack key but instead is replaced with ${TOKEN}. Revisit the previous steps if you're confused.

    Slack will turn off the key if you do accidentally make it public. If that happens, reinstall your app in your workspace.

  3. We can add those steps to our workflow with the following:

    1name: Slack Stop Motion
    2on:
    3schedule:
    4 - cron: "*/20 11-21 * * mon-fri"
    5jobs:
    6 cron:
    7 runs-on: ubuntu-latest
    8 steps:
    9 - uses: actions/checkout@v2
    10 - run: |
    11 chmod +x "${GITHUB_WORKSPACE}/slack-stop-motion.sh"
    12 export TOKEN=${{ secrets.SLACK_TOKEN }}
    13 "${GITHUB_WORKSPACE}/slack-stop-motion.sh"
  4. Our workflow is complete! Make sure you commit your file by clicking "Start Commit" on the right side.

With any luck, we've made it through this tutorial with a running cron job in Github Actions. You can leave your Action alone and it will all work for you. If you check the Actions tab you will see it running every 20 minutes between 7:00AM and 5:00PM.

Final Workflow

ğŸŽ‰ğŸŽ‰ğŸŽ‰ You did it! ğŸŽ‰ğŸŽ‰ğŸŽ‰

With your Slack API Key, your images, and your Github actions, you can now enjoy your very own Slack Stop Motion!

Slack Stop Motion

Feedback and Questions

You can find me on Twitter @kevinguebert and I am open to all questions! You can also find the Github repo where I have my images and code available for reference.

Twitch Livestream

note

The livestream on Twitch was done at the start of the project while I was still learning. Don't follow this word-for-word as things have changed!

```