Slack is fully awesome. At Wehkamp we use it for our internal communication and as a tool for our DevOps. The Slack API allows us to build even more advanced integrations. In this blog I'll explore how to use the API to create stuff like a powerful progress indicator, just by updating a Slack message:
Promise the update
We use Axios as a promise-based HTTP client to connect to Slack. Each call needs an authentication-token and a channel ID (even for private messages). There are two end-points we will be using:
- chat.postMessage - this will send a new message to the channel and give us a message identifier
ts
. We will store and reuse it to update the message. - chat.update - this will update the message that corresponds to the
ts
.
Let's create a function that uses both end-points to send a message:
const axios = require("axios");
function sendMessage(token, channel, ts, msg) {
token = encodeURIComponent(token);
channel = encodeURIComponent(channel);
msg = encodeURIComponent(msg);
const action = ts ? "update" : "postMessage";
const url = `https://slack.com/api/chat.${action}?token=${token}` +
`&channel=${channel}&text=${msg}&as_user=true&ts=${ts}`;
return axios.post(url).then(response => response.data.ts);
}
The function returns the message identifier in the form of a promise. The only thing we need to do is save the message identifier and reuse it when calling the function.
Asynchronous problems
The first problem I ran into had to do with messages not arriving in the same order that I expected. Sometimes I ended up with an earlier message as end-result. And sometimes I wanted to send an update for a message that had not even been created yet. If we want to work asynchronously we need to synchronize the way we send messages.
We need to build a class that will handle the process of sending messages. It will store the message identifier so we can update the message. The class should also make sure that only a single message is being send at the same time.
If a message is being send and a new message comes in, the class should store that new message and send it after the first message has been sent.
But what if a 3rd message comes in? We are only interested in sending the latest message, so we should only store the last message. A pattern like this prevents useless updates / excessive use of the Slack API. Be aware: rate limiting applies to the Slack API, so you might want to consider only updating the message once a second.
Class me!
So let's look at the implementation:
const UpdatableMessage = class {
constructor(token, channel) {
this.token = token;
this.channel = channel;
this.ts = null;
this.message = null;
this.nextMessage = null;
this.sending = false;
}
send(msg) {
// don't send empty or the same message
if (!msg || msg === this.message)
return;
// if a message is being send, set is as the next message
if (this.sending) {
this.nextMessage = msg;
return;
}
this.sending = true;
this.message = msg;
sendMessage(this.token, this.channel, this.ts, msg)
.catch(ex => console.log("Something went wrong: " + ex))
.then(ts => {
this.ts = ts || this.ts;
this.sending = false;
const msg = this.nextMessage;
this.nextMessage = null;
this.send(msg);
});
}
};
By implementing a simple Boolean that checks if a message is being send, we solve most - if not all - of our asynchronous problems.
This class is used in the bot-zero project to show an example of a progress indicator. Go check it out and let us know what you think.

Want to know more about the use and the power of truthy / falsy values in JavaScript, check out this blog. More info about Promises can be found here. More on classes and class expressions here.