Building a Simple Microsoft Teams Command for GitHub

Copy files between GitHub repositories without ever leaving your Microsoft Teams channel.

Dan Moore · Sep 26, 2019

USB stick

I recently ported over a Transposit application from Slack to Microsoft Teams. In both cases, running a command within your chat workspace can copy files from one GitHub repository to another, or within one repo. Way better than a USB thumb drive!

There are three different ways to integrate with Teams to send chat messages:

Transposit lets you fork apps easily, and I was able to reuse all the GitHub logic from the Slack application. So all I really had to do was figure out how to interface with Teams. For this integration, I used a webhook.

The goal was to be able to type a command in Teams: @GithubFileCopier https://github.com/mooreds/repo1/file1.txt https://github.com/mooreds/repo2/file1.txt and have file1.txt be copied from repo1 to repo2. All this without ever leaving the Microsoft Teams interface.

Before you begin

You need to make sure you have privileges to add a webhook to your team. This document can walk you through that.

Then, create a Transposit application. Here's an application you can fork and configure. This application will respond to a webhook call, verify the user, and execute the GitHub file transfer as that user. The readme of that application will walk you through configuring the application and needed data connectors.

OAuth

The OAuth permissions should be as limited as possible. For any application you build, you need to examine available OAuth permissions and only ask for what your application requires. (Here is the GitHub OAuth documentation.) For this application, the only scopes needed are user:email repo read:user. We need the user:email and read:user scope so we can find out the user email address and the GitHub to commit the new file. We need the repo resource so we can actually read and write the files to transfer.

Message details

Each message is verified using HMAC 256. Microsoft Teams calculates this value and sends it along as a header. The Transposit client application re-calculates it when receiving a message and errors if they don't match. This preserves message integrity and makes sure what Teams sent is what we receive.

The Teams chat message is full of information, but what you are most likely interested in is the identity of the sender and the text of the message. Here's the code to parse out that data:

    const parsed_body = JSON.parse(http_event.body);

let command_text = parsed_body.text.trim();

// sometimes we get errant html if someone copy/pastes a command.
command_text = command_text.replace(/<[^>]*>?/gm, '');

const users_team_id = parsed_body.from.id;

...

const text_match = /(\S+) (\S+) (\S+)/.exec(command_text);

We can use the users_team_id to uniquely identify the user communicating with our webhook. And we can take further action on the various values of the text_match array, which will include the filenames that we want to copy.

Usage

If you just type a message to the botname (@GitHubFileCopier) it will respond with a helpful usage message:

Usage: @GitHubFileCopier github-source-file-url github-destination-file-url

But first, please both configure your user at https://ms-teams-github-transfer-l33hg.transposit.io 
and also type, in Teams, "@GitHubFileCopier configure [email-address-you-signed-up-with-in-transposit]"

We configure GitHub credentials via the Transposit user settings page. We need some way to associate the user that is sending the Teams chat message with the Transposit user. At present, the app asks the user to self identify one time. They are sent an email with a verification code, to avoid user spoofing. After responding to the bot with that one time code, the mapping between Transposit Teams is complete for the user.

Then, you can merrily copy files between any repositories to which you have access:

@GitHubFileCopier \
https://github.com/mooreds/test-source/blob/master/file1.txt \
https://github.com/mooreds/test-destination/blob/master/file1.txt

or within the same repository:

@GitHubFileCopier \
https://github.com/mooreds/test-source/blob/master/file1.txt \
https://github.com/mooreds/test-source/blob/master/file2.txt

Build your own

Now you can build a command in Teams to fetch information from APIs from other tools you use. Here are some other ideas of commands you could build:

You can get started by viewing the code I wrote for GitHubFileCopier.