jon's avatar Jonathan Johnson

Handling Complicated Tasks with a Chatbot

This is a text summary of slides for a talk.


Who am I?


Ilios Story


@greeterbot

Your First UCTech Friend

https://github.com/stopfstedt/uctech-greeter


@Zorgbort

Or whatever stupid name you want to give your bot

https://github.com/ucsf-ckm/zorgbort


Automate Everything

If you&##39;re scared to deploy on Fridays, you should be scared to deploy ever.

Don&##39;t you have tests? Backups? Roll back? Come on.

— Jessica Mauerhan (@JessicaMauerhan) July 27, 2018

Automation Brings Speed

and sometimes money!


Our Automation Goal

Tag Code

  1. Tag Code as v65.4.3
  2. Go To Lunch
  3. v65.4.3 is now in Production
  4. Repeat

Zorgbort is just the fancy UI

The hard part is building automated processes

But knowing a UI can be built can make the automation process simpler


Git tags


Slack


Node.js

Easy to deploy lots of places

Botkit (and many other) frameworks

Cuz javascript is the BEST!


Home is where the bot lives


Slackbot anatomy

… first of all they’re called apps now


> Don't call us, we'll call you > Subscribe to the event types you need > Governed by OAuth permission scopes > Bot users served here > Everything is eventual

https://api.slack.com/


Don’t call us, we’ll call you

POST

{
  "token": "XXYYZZ",
  "team_id": "TXXXXXXXX",
  "api_app_id": "AXXXXXXXXX",
  "event": {
          "type": "name_of_event",
          "event_ts": "1234567890.123456",
          "user": "UXXXXXXX1",
          ...
  },
  "type": "event_callback",
  "authed_users": [
          "UXXXXXXX1",
          "UXXXXXXX2"
  ],
  "event_id": "Ev08MFMKH6",
  "event_time": 1234567890
}

Everything is eventual

POST /api/chat.postMessage

{
  "channel": "C061EG9SL",
  "text": "I hope the tour went well, Mr. Wonka.",
  "attachments": [
    {
      "text": "Who wins the lifetime supply of chocolate?",
      "fallback": "You could be telling the computer exactly what it can do with a lifetime supply of chocolate.",
      "color": "##3AA3E3",
      "attachment_type": "default",
      "callback_id": "select_simple_1234",
      "actions": [
        {
          "name": "winners_list",
          "text": "Who should win?",
          "type": "select",
          "data_source": "users"
        }
      ]
    }
  ]
}

Botkit

Our Hero

const Botkit = require('botkit');

const hal = new Botkit();
hal.hears('Open the pod bay doors', async (bot, message) => {
  await bot.reply(message, "I'm Sorry Dave, I'm afraid I can't do that.");
});

Intelligent Conversation

const Botkit = require('botkit');
const Skynet = require('skynet');

const controller = new Botkit();
controller.hears(async (bot, message) => {
  const reply = Skynet.process(message.text);
  await bot.reply(message, reply);
});

Don’t trust the Developers at SlackHQ

Slack should be a thin layer on top of functionality

Because the slack API will change

Not without warning

@Zorgbort

module.exports = (bot) => {
  bot.hears(
    ['start release', 'release'],
    ['direct_message', 'direct_mention', 'mention'],
    startRelease,
  );
  bot.on('interactive_message_callback', chooseReleaseType);
  bot.on('interactive_message_callback', confirmRelease);
  bot.on('interactive_message_callback', doRelease);
};

https://github.com/ucsf-ckm/zorgbort


@Zorgbort

chooseReleaseType

const startRelease = async (bot, message) => {
  bot.reply(
    message,
    createActionReply(':cool: I just need to know:', releaseProject, [
      {
        name: 'project',
        text: 'Which Project?',
        type: 'select',
        options: [
          {
            text: 'Ilios Frontend',
            value: 'frontend',
          },
          {
            text: 'Ember Simple Charts',
            value: 'simple-charts',
          },
        ],
      },
    ]),
  );
};

https://github.com/ucsf-ckm/zorgbort


@Zorgbort

releaseAndTag

const releaseAndTag = async (owner, repo, releaseType, namer) => {
  const { nextVersion, currentVersion } = await incrementPackageVersion(dir, releaseType);
  await commitAndTag(dir, version, releaseName);
  const release = await Github.repos.createRelease({
    owner,
    repo,
    tag_name: version,
    name: releaseName,
    body: releaseNotes,
    draft: false,
    prerelease: false,
  });

  return {
    version,
    releaseName,
    releaseUrl: release.data.html_url,
  };
};

https://github.com/ucsf-ckm/zorgbort


Start the Conversation

Send Alerts

Start a Virtual Standup

Remind Yourself to Drink More Water

await bot.startConversationInChannel(SLACK_CHANNEL_ID);
await bot.say('The Server is On Fire!!! :fire:');
await bot.say('Everybody Blame Jon');
await bot.say("PS: don't tell Jon!");

Security

Depends on the specific threat

Slack requires token auth and SSL

You may need to validate users yourself


Authorize Users

if (validUsers.includes(user)) {
  try {
    return await releaseAndTag(owner, repo, releaseType, namer);
  } catch (e) {
    bot.reply(message, `Error: ${e.message} (stack trace in logs)`);
    console.error(e);
}

Testing locally

$ npm start
> zorgbort@1.0.0 start
> node zorgbort.js

Initializing Botkit v0.7.4
info: ** Using simple storage. Saving data to .data/db/
info: ** Starting webserver on port 3000
info: ** Serving webhook endpoints for Slash commands and outgoing webhooks at: http://0.0.0.0:3000/slack/receive
info: ** Serving login URL: http://0.0.0.0:8899/login
info: ** Serving oauth return endpoint: http://0.0.0.0:3000/oauth

So how to we hookup slack to port 3000?

$ npm install -g localtunnel
$ lt --port 3000 -s zorgbort-stage
your url is: https://zorgbort-stage.localtunnel.me

Screenshot of Slack's Interactive setup process


Let’s Review

Bots Are Cool

But They’re Really Just Window Dressing