enter image description here

Introduction

I am a Microsoft Student Partner at the Microsoft Innovation Center from the “Politehnica” University of Bucharest.

Part of our responsibility is giving technical presentations to students about various technologies. We also have a lab at the University where we give the presentations and spend most of our time and where other students can come and work on their projects.

Up until in September, we used Facebook for almost all of our communication (I know, my feelings as well).

So we had a Facebook group for our communication inside the team (which was a total mess since it was a single chat channel) and people would ask us questions on our Facebook page.

Introducing Slack

enter image description here

In September, I thought it was high time we changed the way of communicating, so each of us came up with solutions.

People inside the team proposed Skype or keeping Facebook, while I had the idea to try Slack for a few weeks and decide if we liked it (the question was wether the team was going to like it, I was sold on it).

So we used Slack for a week or so, until (most of) of the team was (almost) satisfied with it.

A few months passed by and we had grown into using Slack for all of our internal communication, for programming, team management or totally random stuff.

Our custom requirements

Then, we decided we needed to advertise the fact that people can come in our lab and work on their projects.

So we needed a way to communicate with people not using Slack (since they were not part of our team but random students), and inviting them on the team was not an option (for the obvious reason – we didn’t want random people in our Slack team).

We also considered solutions like this one that add a button automating Slack invites, but people needed to ask quick questions answered within minutes – so creating and confirming a Slack account was, again, not an option.

Creating the custom integration

So we decided to create our custom Slack integration.
Since I am familiar in developing web applications using the .NET Framework, it seemed only natural to create this integration using Asp.Net WebApi as web server, SignalR for real-time communication and an Azure SQL database as message store.

enter image description here

The solution consisted of two parts:

Posting messages to a Slack channel from a public web page

As I said earlier, posting messages to a channel from the outside was done using Incoming Webhooks.
I set up an incoming webhook for a specific channel and received an URL that I used to post the messages to and was passed to the SlackClient constructor.

Basically, what SlackClient does is a POST request to the URL provided by the Incoming Webhook with the message as payload (the method that actually sends the message to Slack is PostMessage from SlackClient).

This is the actual controller method that gets executed from the UI when somebody sends a message.

[HttpPost]
public void PostMessageToSlack(SlackMessage message)
{
    SlackClient.PostMessage(message);
    ConnectionManager.GetHubContext<SlackHub().
            Clients.All.addMessage(message.UserName, message.Text);
    SlackMessageStore.SaveMessage(message);
}

 

This line is responsible for the real-time communication – this means that all messages from the public web page are seen by all connected users.

ConnectionManager.GetHubContext<SlackHub>().
Clients.All.addMessage(message.UserName, message.Text);

This is the PostMessage method implementation from SlackClient.

public void PostMessage(SlackMessage message)
{
    string jsonMessage = JsonConvert.SerializeObject(message);

    using(WebClient webClient = new WebClient())
    {
        NameValueCollection data = new NameValueCollection();
        data.Add("payload", jsonMessage);

        webClient.UploadValues(Uri, "POST", data);
    }
}

 

This method simply makes a POST request with the message to the URI received from Slack.

Posting messages from Slack to the public web page

When setting up a slash command, you are asked to provide a URL where Slack will create a POST request with the content of the command (including the message).

Among other things to consider (you should always check the command token to make sure the command you received is from your team), your method (in my case the PostMessageToWeb method from the SlackController) receives the command and has to publish it to the public web page and back to the Slack channel.

[HttpPost]
public void PostMessageToWeb([FromBody]JToken commandRequest)
{
    var command = JsonConvert.DeserializeObject<SlackCommand>(commandRequest.ToString());

    if(command.Token == ConfigurationManager.AppSettings["SlackCommandToken"])
    {
        ConnectionManager.GetHubContext<SlackHub>
                ().Clients.All.addMessage(command.UserName, command.Text);
        SlackClient.PostMessage(text: command.Text, userName: command.UserName);
        SlackMessageStore.SaveMessage(text: command.Text, userName: command.UserName);
    }

}

 

This method receives the command request from Slack, verifies that it actually came from your team, broadcasts the message to all connected clients on the web page, sends the message back to the Slack channel and saves the message on the Azure SQL database.

Testing and publishing the app

You can test the part of the app that publishes messages from the web page to Slack locally. But if you want to test the part that sends messages from Slack, you need to have a publicly accessible URL that Slack needs to send the commands to.

So I published it as an Azure WebApp and gave Slack the URL to send commands: /api/Slack/PostMessageToWeb.

Building the project

You can find a GitHub repo with the project
here.

In order to build the project, you need to add two .config files in the root of the API project:

  • appSettings.config
    <?xml version="1.0" encoding="utf-8" ?>
    <appSettings>
    <add key="SlackWebHookUri" value={web_hook_uri}/>
    <add key="SlackCommandToken" value={command_token}/>
    </appSettings>
  • connectionStrings.config
<?xml version="1.0" encoding="utf-8" ?>
<connectionStrings>
<add name="SlackDbConnectionString" connectionString = {connection_string} />
</connectionStrings>

If you don’t need it, you can exclude the message store and the SQL Database, but you must remove the dependencies for SlackMessageStore and for SlackDbContext, and the not register SlackDbContext with the builder.

builder.RegisterType<SlackDbContext>()
.WithParameter("connectionString", ConfigurationManager.ConnectionStrings["SlackDbConnectionString"].ConnectionString)
.InstancePerRequest();

Next steps

  • create a UI for the public web page
  • create support for automatic responses
  • add support for attachments in messages

Conclusion

The whole idea behind this integration was to create a simple way for people outside our team to communicate in real-time.
The solution was a WebApi solution with SignalR for real-time communication and an Azure SQL for the message store.