loading words...

Jun 15, 2019 07:23:55

Integrating Slack in a Website

by @basilesamel PATRON | 1278 words | 326🔥 | 368💌

Basile Samel

Current day streak: 326🔥
Total posts: 368💌
Total words: 179937 (719 pages 📄)

Instant communication channels have been around for a long time. Their business value cannot be ignored: you can build entire communities from a chat application. Telegram, Slack, or Discord have become mainstream tools in most organizations.

Relying on free external services has its limits though, most users do not want to install anything or create an additional account. It's especially true for tech products such as web applications: it's already hard to convince a user to use your product, why would he/she want to subscribe to another service within it? It feels redundant, it's not practical.

On the other hand, implementing a chat application is no trivial task. The user interface quickly gets out of hand, messages must be loaded in near real-time, you need to keep track of the user presence, messages must be stored... it's an entirely different area of expertise that comes with heavy operational costs.

That's where the chat integration comes in, it allows you to get the best of both worlds: less coupling in your infrastructure, better features, and more importantly, increased interactions between your users.

To give you a concrete example, 200 Words a Day is an online community based on its own website. Soon after the minimum viable product phase I decided to establish a Slack workspace in parallel, to interact with the members in a different way. Out of the 2800 members who registered on the platform, 130+ users joined the Slack workspace. That's only a 5% conversion rate.

Progressively moving off Slack appeared as the most straight-forward way to increase interactions between members. The issue is I don't want to build a chat app from scratch. I tried already in a previous project and it wasn't easy. I do not want to increase my server costs either. The best approach was to use Slack's API to create a client within 200WaD. This way, users who didn't registered in Slack can still read what's going on, which in turn should result in a higher conversion rate.

1. Installing a Slack App

Log in Slack and head to api.slack.com/apps to create your Slack client. You will need a workspace ready with admin rights.

Once the app has been set up, you obtain a Client ID and a Client Secret.

SLACK_CLIENT_ID=XXXXX
SLACK_CLIENT_SECRET=XXXXX

Add a bot user by clicking on the "Bot Users" tab in the left menu.

Navigate to the "OAuth & Permissions" page of your Slack application to add some permission scopes. Let's start with channels:history, channels:read, chat:write:user, groups:history, groups:read, incoming-webhook, bot, users:read, and users:read:email. Those scopes will allow us to write and read from/to public and private channels. Save the changes and hit "Install App to Workspace".

You will be prompted to a confirmation page. Choose any channel to confirm the installation - it doesn't matter which one - and click "Install".

You are redirected to the OAuth & Permissions page. Copy both your OAuth Access Token and your Bot User OAuth Access Token.

SLACK_OAUTH_ACCESS_TOKEN=xoxp-XXXXXXXX
SLACK_BOT_OAUTH_ACCESS_TOKEN=xoxb-XXXXX

You just need to configure one thing in the same page: a redirect URL. The Redirect URL is used in the login process, it's usually the URL you use to enter your chat application from within your website. For example, my local environment uses http://localhost:8000/chats, but I have another Redirect URL (https://200wordsaday.com/chats) in my production environment. Add both and save the parameters.

You now have a client ready to interact with your Slack workspace.

2. Implementing a Slack Client

A. Storing and Syncing Users

In order for your visitors to write to your Slack client, they need to be authenticated.

Slack users are described by a unique identification number attached to their Slack account, and they can read/write from/to the Slack workspace by using a personal access token.  Ideally, you want the authentication process to be as painless as possible: authenticate once, store the user access token and identification number in your web app database, and use them whenever it's needed.

Chances are your web app and your Slack workspace have two distinct sets of members: you need to synchronize the two user bases by using the identification number. You can for example query users based on the ids you receive, then request the email addresses to match them with the ones stored in your own database. This way your application can understand who is who based on the Slack identification number.

Sometimes the emails do not match. A user might want to use a different email address for example. Email matching is a trick to pre-populate your database, but you still need your user to authenticate from within your app.

That's where the "Sign in with Slack" button comes in. It's the only way for your user to interact with your Slack client.

B. Sign in with Slack

The "Sign in with Slack" button allows your user to authenticate: to access direct messages, private channels, and write messages.

When a user arrives to your web chat page - represented by the Redirect URL we configured in part 1 (ie localhost:8000/chats) - we need to fetch an access token to communicate with Slack's APIs. Clicking on the Sign in with Slack button redirects the user to a authentication page, which returns data about the users to your web application: the user identification number, and the access token.

You can retrieve them once and store them in your database for further use.

Of course, before signing in, a user must have an account in your workspace. If that's not the case, you can display an invite link to register new members.

After logging in Slack, the user is redirected to your web application along with an authentication code parameter as a GET variable, according to the Redirect URL you specified.

You can exchange this authentication code for an access token by calling the oauth.access endpoint of the Slack API:

public function authenticate($code){
return $this->get("https://slack.com/api/oauth.access", array(
'client_id' => getenv("SLACK_CLIENT_ID"),
'client_secret' => getenv("SLACK_CLIENT_SECRET"),
'code' => $code,
'redirect_uri' => 'http://' . getenv('HOST') . '/chats'
));
}

$response = $this->authenticate($code);
echo $response['access_token']; // => your user's access token
echo $response['user']['id']; // => Slack user id

The user is now able to use Slack's APIs.

C. Understanding Slack's APIs

Slack's Application Programming Interface is in fact a set of several atomic APIs: Web API, Events API, Conversations API, and Real Time Messaging (RTC) API.

In this tutorial, we are going to use all of them to implement specific features.

The Web API is used to send rich messages. Not just text, but also pictures, links and file attachments.

The Events API is used to be notified when you receive direct messages.

The Conversations API allows us to access the message history of any channel or private conversation.

The RTC API is a websocket-based API used to know which users are online. Its main purpose is to receive channel messages in near real-time. It complements the Events API to notify our application of anything happening in our Slack workspace.

3. Main Features

A. Listing Conversations

There are four conversation types: public channels, private channels, group conversations, and direct conversations.

Channels have no theorical user limit. A group conversation (mpim) can gather 9 users top. A direct conversation (direct message, im) is between two users.

All you need to retrieve the list of conversations is to call the API endpoint conversations.list:

$conversations = $this->get("https://slack.com/api/conversations.list", array(
'token' => $this->getToken($user), // (1)
'types' => $types, // (2)
'exclude_archived' => true // (3)
));

(1): If your user is not authenticated, use your own app oauth access token. Else, let the client uses his/her own.

private function getToken($user){
$token = getenv("SLACK_OAUTH_ACCESS_TOKEN");
if($user->isAuthenticated())){
$token = $user->getSlackAccount()->getToken();
}
  return $token;
}

(2): If your user is authenticated in your web client you can display all the related conversations. Otherwise, you just want to fetch public conversations. This is what the $types variable specifies:

$types = 'public_channel';
if($user->isAuthenticated()){
$types = 'public_channel,private_channel,mpim,im';
}

(3): Set to true to exclude archived channels

Slack then returns a list of conversations we can use to read or send messages.

B. Retrieving messages of a given conversation

C. Retrieving members of the workspace

D. Formatting messages

E. Sending messages

F. Receiving messages in real-time

G. User presence

From Basile Samel's collections:

contact: email - twitter / Terms / Privacy