June 30, 2015
by Patrick Hoolboom
Slack is an amazing tool but sending invitations was a little bit of a pain point for us. So we figured (like we do), let’s automate it! Before we dig in to how we did this, if you haven’t signed up for the StackStorm-Community, do it now: StackStorm-Community
First, I’d like to thank the academy…wait…wrong speech. In all seriousness, I found the following blog and it made writing these automation so simple. I have to give credit where credit is due:
levels.io/slack-typeform-auto-invite-sign-ups
So go tweet at him, or send him chocolates or ponies. He deserves it!
We have been using Slack for internal communication for quite a while now. We love it. The search functionality, the doc uploads, all of it is fantastic for us. As we began maturing our ChatOps story, we did almost all of that development work through Slack.
Conversely, a majority of our customer interactions were done via Google Groups or our IRC channel (#stackstorm on Freenode). Neither of these methods were inherently bad but they missed a lot of the fun features you get from using Slack or another rich chat client. Who doesn’t want images to automatically show up in the room when a link is posted? 🙂
So as we were working on a way for people to test out our ChatOps integration we decided to use a separate Slack organization. We got all the fun features of Slack plus we also easily have a StackStorm bot in the room with all sorts of neat actions for people to use.
Beyond a working StackStorm installation, the following four integration packs are required for this to work.
First, it wasn’t easy figuring out exactly what a “public” Slack organization would be. I kept thinking there would be some specific designator for this, but there isn’t. So we spun up:
stackstorm-community.slack.com
By default, only people with email addresses from the domain you specified when setting up the org could sign up through the Slack interface. In order to invite the community, we needed an admin to send them an invitation. This started to smell like a good opportunity for automation.
After reading through @levelsio’s blog I knew how he had done it, but I wanted to do it a little different. This was the design I had in mind:
This seemed simple enough. I started out writing the Typeform sensor using the API endpoint information I had gotten from the blog as a jumping off point.
If anyone wants to skip over my beautiful prose and just read code, the sensor is located here:
I needed a way to validate that the sensor only emitted triggers on new user registrations. I realized that Slack will not send an invitation to the same email address more than once so I used email as my uniqueness constraint. The sensor retrieves the completed list of submissions from the Typeform API, then queries the MySQL database to see if that user is already in there. If they are, it skips them. Otherwise, it emits a trigger with the new user information.
For the sensor metadata, you can see that the parameters all map to fields on the form (except the date_* fields which are metadata sent from the Typeform API) but the only one required is email. This matches the setup of the form.
---
class_name: "TypeformRegistrationSensor"
entry_point: "registration_sensor.py"
description: "Sensor which monitors for new Typeform registrations"
poll_interval: 60
trigger_types:
-
name: "registration"
description: "Trigger which indicates a new registration"
payload_schema:
type: "object"
properties:
email:
type: "string"
required: true
first_name:
type: "string"
last_name:
type: "string"
source:
type: "string"
newsletter:
type: "string"
referer:
type: "string"
date_land:
type: "string"
date_submit:
type: "string"
NOTE:
I had created the MySQL database prior to writing the sensor. In order to use this yourself, you will need to follow the Typeform integration pack README.
Configuration
The Typeform pack requires a bit of configuration. You’ll need to go to the admin page of your Typeform account and get your API key. You’ll also need to pull the form id from the URL of your Typeform form. The URL looks like this: https://stackstorm.typeform.com/to/K76GRP
In our case, the form ID is K76GRP
Add both the API key, and the form id to your Typeform pack config.yaml. Also add in the credentials for your database while you are there.
An interesting side effect of revisiting our Slack integration was that we ended up with a whole bunch of shiny new Slack actions! The new Slack pack is located here:
The action that matters for this use case is slack.users.admin.invite
. This action lets us send an invitation to our Slack organization to any email address, whether or not the domain matches the one we set the organization up for. Woohoo!
This is another chance for me to plug @levelsio’s blog. The admin API is not documented. He had discovered this and saved me quite a bit of work. 🙂
Now, this does require a little set up on the Slack side. You’ll need to get an admin api token and add it to the admin section of the Slack pack config.yaml. The admin API token is slightly different than the API token used to access the other actions. You’ll need to create an application at the following link and use that token: Slack Applications
Also in the admin section of the config.yaml, you will need to configure your Slack organization name. This is the name as it appears in the beginning of the organization’s url. In our case, it would be stackstorm-community
.
munity.slack.com
This part is pretty straight forward. An action chain that writes the data to the database and sends the slack invite…two sequential steps.
Action Chain: register_and_invite.yaml
---
chain:
-
name: "insert_registration"
ref: "mysql.insert"
params:
db: "community"
table: "user_registration"
data: "{{registration_data}}"
publish:
email: "{{registration_data.email}}"
first_name: "{{registration_data.first_name}}"
on-success: "send_slack_invite"
-
name: "send_slack_invite"
ref: "slack.users.admin.invite"
params:
email: "{{email}}"
first_name: "{{first_name}}"
default: "insert_registration"
Action Metadata: register_and_invite.yaml
---
name: "register_and_invite"
runner_type: "action-chain"
description: "Send Slack invitation based on Typeform submissins"
enabled: true
entry_point: "chains/register_and_invite.yaml"
parameters:
registration_data:
type: "object"
required: true
description: "Registration data as formatted when sent from Typeform"
Simple enough.
This was also quite easy to write. Nothing magic here.
---
name: "typeform_invite"
enabled: true
description: "Write to DB and send invite on new user submission"
trigger:
pack: "typeform"
type: "typeform.registration"
criteria: {}
action:
ref: community.register_and_invite
parameters:
registration_data: "{{trigger}}"
And that’s it. Users can now fill out the Typeform form and get an invitation to the StackStorm-Community Slack Org! Overall, a pretty simple process. One really big aspect of automating this through the StackStorm platform is the visibility I have through the CLI or UI. If a complaint comes in that their invitation hasn’t arrived yet, I can check the status of the workflow through the Web UI or CLI. Or even get visibility in to the actual trigger that was emitted by the sensor through the trigger-instance list
functionality we recently added to the CLI. So, everyone come sign up! StackStorm-Community
Also, feel free to tweet about us @Stack_Storm, contact us at moc.mrotskcatsnull@troppus, or even check out the IRC channel on Freenode #stackstorm. Though, if you use the last one we’ll probably point you back to the StackStorm-Community Slack Channels!