Jordi Cabot
Posted on September 17, 2019
Sustainability of open source is a huge problem. There are many ways to help the maintainers but one growing trend is the creation of bots that automate some of the tasks. See a good list of existing bots you can add to your OSS project.
In this post, we'll show you how you could easily build your own bot for GitHub. Not just a "simple" bot, but a chatbot!. Once we're done you'll be able to chat with your GitHub repositories. To make things even more interesting, we'll deploy the chatbot as a Slack app. As example bots, we'll create two chatbots:
- (for users) A bot that helps your users write good bug reports so that you don't waste your time trying to reproduce their bugs with incomplete information
- (for maintainers) A bot that pings you on Slack when a new issue is created and lets you assign labels and developers to it.
To create such a chatbot you'll need to interact with the Slack API, the GitHub API and at least a Natural Language Understanding (NLU) component, like DialogFlow. Instead of manually doing all this work ourselves, we will rely on Xatkit platform (an open-source low-code chatbot development framework). Xatkit already provides an abstraction of these three elements, heavily simplifying the development of chatbots, as you'll see in a moment (for the TL;DR people, check the Xatkit organization in GitHub and take a look at the code of the example bot).
In Xatkit, you define the bot behaviour via two different files: the intent library (that specifies the potential intentions of the user when talking to the chatbot) and the execution model (that defines how to react to the user intentions or other events directly received by the bot). Let's see how we can use Xatkit to create our two bots.
Filing a bug report from Slack
If you look at any OSS project, the firsts iterations in any new bug report consist in the maintainer trying to get from the user who submitted the report all the information needed to reproduce it. This is a huge waste of time for both sides. And one that delays significantly the bug fixing since the communication is not synchronous so depending on the respective availability and time zones, it can go on for a few days.
I'll show you how a chatbot can make sure a bug report has all the basic information a maintainer needs. In the example, we assume the project is a WordPress plugin. Typically, to debug a potential error there we always need to know the WP version and the PHP version the website is running. So to illustrate the capabilities of our chatbot approach for GitHub we'll make sure the chatbot collects this information.
We start by defining the intents. Note that some of the intents are only possible after a previous intent has been matched. Some of the intents store conversation data that we'll use to open the bug at the end.
intent OpenBug { inputs { "The plugin is not working" "I have a problem with the plugin" "I'd like to report an error" "I want to open a bug" "I want to report a bug" "There is an error in the plugin" } } intent DescribeBug follows OpenBug { inputs { "My error is Error" "The problem is Error" "I get this error: Error" "My error is that Error" "The problem is that Error" "I get the error Error" } creates context bug { sets parameter title from fragment Error (entity any) } } intent TellWPVersion follows DescribeBug { requires context bug inputs { "My version number is WPVersion" "I use number WPVersion" "It's version WPVersion" } creates context bug { sets parameter wpversion from fragment WPVersion (entity number) } } intent TellPHPVersion follows TellWPVersion { requires context bug inputs { "My version is PHPVersion" "I use PHPVersion" "It's version PHPVersion" "The server is on php version PHPVersion" } creates context bug { sets parameter phpversion from fragment PHPVersion (entity number) } }
This is an example dialog with this chatbot.
And once all the error info has been gathered, we just need to put it all together and open an issue with it. As you can see in the following code excerpt, once the last intent has been matched we ask GitHub to create a new issue with the title and body parameters we collected along the way. We end up labeling the new issue as a bug.
on intent TellPHPVersion do action SlackPlatform.Reply(message : "Thanks for your detailed info") def newissue = action GithubPlatform.OpenIssue(user : "jcabot", repository : "xatkit-tests", issueTitle : context(bug).get("title") , issueContent : "WP version is " + context(bug).get("wpversion") + " PHP version is " + context(bug).get("phpversion") ) action GithubPlatform.SetLabel(issue : newissue, label : "bug")
This generates as end result the following issue created from the above dialog.
From GitHub to Slack: receiving and reacting to GitHub notifications
Let's now how we can get notified in Slack of things happening in GitHub. In this example, we'll just look at "New issue" notifications. The bot should be able to:
- Receive New Issue notifications from GitHub
- Save time to the maintainer by enabling her to assign a label to the issue and a potential developer that could follow-up on it
To process the OpenIssue notification and forward it to Slack, we just need to write this piece of code
on event Issue_Opened do action SlackPlatform.PostMessage(message : "An issue has been opened with the title " + context(issue).get("issue->title"), channel: config(slack.channel)) def issue = action GithubPlatform.GetIssue(user : config(github.repository.username), repository : config(github.repository.name), issueNumber : context(issue).get("issue->number")) session.store("issue", issue)
where we get the issue information from the event and post it to Slack (SlackPlatform is part of the predefined Slack platform in Xatkit). The issue itself is also stored in the session for further use.
Once the maintainer gets the notification, the chatbot starts listening to see if the maintainer expresses any intention to add a label or assign a user to it. These intents definitions will try to match whatever the maintaner writes in Slack to these two potential actions.
intent SetLabel { requires context issue inputs { "Set label Label" "Give label Label" } creates context issue { sets parameter issueLabel from fragment Label (entity any) } } intent AssignUser { requires context issue inputs { "Assign user Username" "Username will take care of it" } creates context issue { sets parameter assignedUsername from fragment Username (entity any) } }
Note that, as for the previous bot, intents are defined via a set of training sentences. Training sentences try to represent the different ways a maintainer can express that intention (e.g. set label or give label). Thanks to our DialogFlow integration, additional variations will also be recognized: see the following dialog, where I write "Add label" instead of Set or Give.
After this chat, the new issue gets properly tagged and assigned in the GitHub repo. No need for the maintainer to move out of Slack to do so. The execution actions associated with the intents take care of this.
on intent SetLabel do action GithubPlatform.SetLabel(issue : session.get("issue"), label : context(issue).get("issueLabel")) action SlackPlatform.Reply(message : "Done!")
Summary: chatting with GitHub
To sum up, in a few lines of code we've been able to create a bidirectional Slack-GitHub chatbot to help open source maintainers. This was just an example but I hope it's a good enough example to showcase the potential of (chat)bots to significantly improve the sustainability of open-source.
Posted on September 17, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.