Intro to ChatOps with Lita – An Upstate Ruby lightning talk



Intro to ChatOps with Lita – An Upstate Ruby lightning talk

0 0


upstate-ruby-lita-talk

Slides for Upstate Ruby lightning talk: Intro to ChatOps with Lita

On Github ryoe / upstate-ruby-lita-talk

Intro to ChatOps with Lita

An Upstate Ruby lightning talk

May 25, 2016

Rick Yoestingryoe on GitHub@ryoe_ok on Twitter

About me

Places I have not worked

  • GitHub
  • StackStorm
  • Atlassian
  • FlowDock
  • PagerDuty
  • VictorOps

About me

Places I have worked

A number of companies that are not world famous. (Yet?)

Currently, Clown Shoes Wrangler at RaiseMore.

Why listen to me?

I gave a lighning talk on Hubot scripts in Aug. 2014 and after all that time... 4 stars. I guess you could say I'm kind of an expert. ;-)

ChatOps

What is it even?

Chat connects people near and far. At it's core, ChatOps is about communication, transparency, automation, and feedback. It's about bringing tools into the conversations you're already having in chat. But it's not all about work... it's also about fun! And building company culture.

Communication

"Conversation Driven Development" Chat is where the action is... when the building is on fire! and when the good times roll! It's where the people are.

Automation

  • Repeatable
  • Reliable
Automating tricky, cumbersome or error-prone tasks is always a good idea. Write a script for it and run it from chat. This makes such a process repeatble and realiable.

Transparency

Web hooks from external sources providing info. Surfacing information to make informed decisions. Run a report. Show a graph. Error logs. Commands to retrieve info, perform tasks and report those results. All of this feedback happens in chat for all to see and learn from. In this way training is "free."

Fun

Never discount the value of fun! We're all people.

ChatBots

Choices

  • Hubot - CoffeeScript, JavaScript
  • Lita - Ruby
  • Err - Python
ChatBots for when you're ready to do more than just receive incoming web hooks. Choose what's best for your team in terms of development language and feature set.

ChatBots

What it do?

  • Hear
  • Respond
ChatBots primarily do two things... Hear - "listen" for words/phrases, then take action Respond to commands - perform tasks of some kind when specifically instructed to do so

Hear

Example 1

In this case, using feature provided by Slack. You don't have to roll your own for everything.

Hear

Example 2

"custom" hear tailored to our culture.

Respond

Example 1

Queue a build... so easy, anyone can do it.

Respond

Example 2

Internal tooling...

Respond

Example 3

Misc. curated links for various systems/environments.

Respond

Example 4

:-D

Respond

Example 5

:-D Tell story of all night rollouts...

WARN:

Regular Expressions

Love 'em or hate 'em, you can't avoid 'em

If you do hate 'em...

All routing is handled via RegEx, so...

I've got you!

<3 <3 <3

If you need help, I got your back!

Hello, Lita

Lita.io/

:-D

Why Lita?

  • Runs on Ruby
  • Many chat adapters available
  • Leverage Ruby ecosystem:
    • RubyGems
    • Bundler
:-D

Requirements

  • Ruby 2.x
    • or JRuby 9.0.0.0+
    • or Rubinus 2+
  • Redis 2.6+
:-D

Install Lita

 

gem install lita

:-D

Create your bot

lita new

$ lita new
      create  lita
      create  lita/Gemfile
      create  lita/lita_config.rb
:-D

Run your bot

lita start

By default, lita is configured to use the shell adapter and does pretty much nothing else... help command is about the extent of things.

Name your bot

lita_config.rb

Lita.configure do |config|

  # The name your robot will use.
  config.robot.name = "Johnny Five"

end
:-D

Name your bot

Action shot

:-D

Plugins

Getting more out of Lita

  • Adapters
  • Handlers
  • Extensions
Adapters - used to connect to a chat service. Handlers - add new runtime functionality Extensions - add new functionality to Lita's core. From Lita docs: Note: Extensions are an advanced topic and most of the time you won't need to write them yourself.

Adapters

* Slack * HipChat * Flowdock * IRC * Campfire * Chatwork * Facebook * Gitter * Telegram * idobata * Telegram (another!) * Tox * Twitter * VKontable * Discord

https://plugins.lita.io/#adapters

Lita adapters are used to connect to a chat service such as Slack, HipChat or IRC. Creating an adapter is beyond the scope of this talk, but you *can* do it if you really want to.

Make a chat bot

Because Lita is better with friends!

Adapter Config

lita_config.rb

Lita.configure do |config|
  config.robot.adapter = :slack
  config.adapters.slack.token = "my-token"
end

Gemfile

source "https://rubygems.org"

gem "lita"
gem "lita-slack"
This example is Slack. There are several other config options, so always consult the docs for your chosen adapter. Add the gem, bundle install and lita start!

Handlers

What it do?

  • Chat routes
  • HTTP routes
  • Event routes
Chat routes - your basic "hear" and "respond" HTTP routes - make HTTP requests to Lita's built-in web server (Faraday client, Rack::Request, Rack::Response) Event routes - Lita has built-in pub/sub system. Handlers can communicate and/or respond to arbitrary system events.

Handlers

  • Install a gem*
  • Roll your own

*plugins.lita.io/#handlers

:-D

Chat route "hear"

handlers/ping.rb

module Lita
  module Handlers
    class Ping < Handler
      route(/ping/, :pong, 
        help: { "ping" => "replies back with PONG" })

      def pong response
      	response.reply "PONG"
      end

      Lita.register_handler(self)
    end
  end
end

lita_config.rb

require './handlers/ping.rb'

Lita.configure do |config|
  # my config
end
:-D

Chat route "hear"

command defaults to false

module Lita
  module Handlers
    class Ping < Handler
      route(/ping/, :pong,
        command: false, # defaults false
        help: { "ping" => "replies back with PONG" })

      def pong response
      	response.reply "PONG"
      end

      Lita.register_handler(self)
    end
  end
end
:-D

Chat route "respond"

handlers/gnip.rb

module Lita
  module Handlers
    class Gnip < Handler
      route(/gnip/, :gnop,
        command: true, # "respond"
        help: { "gnip" => "replies back with GNOP" })

      def gnop response
        response.reply "GNOP"
      end

      Lita.register_handler(self)
    end
  end
end

lita_config.rb

require './handlers/gnip.rb'
# the rest...
command: true makes it "respond" only

Chat routes in action

Hear

Lita > I say ping
PONG
Lita > lita ping
PONG

Respond

Lita > I say gnip
Lita > lita gnip
GNOP
"Hear" is basically the honey badger! It don't care!

Chat routes

response helpers

reply send message back to source (room or user) reply_privately send message back to user matches array of Regexp matches match_data MatchData from Regexp args array of args with the "command" removed message message object user user object extensions arbitrary data from Lita extension

Helpers example

handlers/helpers.rb

module Lita
  module Handlers
    class Helpers < Handler
      route(/helper[s|z] (\d*){1}/i, :puts_helper, command: true)

      def puts_helper response
        puts "matches: #{response.matches}"
        puts "match_data: #{response.match_data}"
        puts "args #{response.args}"
        puts "message: #{response.message}"
        puts "user: #{response.user}"
        puts "extensions: #{response.extensions}"

        response.reply_privately "Shh! This is private!"
        response.reply "Hola!"
      end

      Lita.register_handler(self)
    end
  end
end

helpers.rb output

Lita > lita helpers 42
matches: [["42"]]
match_data: helpers 42
args ["42"]
message: #<:message:0x007fbb91398da8>
user: #<:user:0x007fbb91a362a0>
extensions: {}
Shh! This is private!
Hola!
Lita > lita helpers 42 21
matches: [["42"]]
match_data: helpers 42
args ["42", "21"]
message: #<:message:0x007fd06c333dc8>
user: #<:user:0x007fd06c34a1e0>
extensions: {}
Shh! This is private!
Hola!

Using args helper

handlers/stars.rb

module Lita
  module Handlers
    class Stars < Handler
      route(/star[s|z] (\d*){1}/i, :star_rating, command: true,
        help: { "stars <num>" => "replies with your star rating" })

      def star_rating response
        starz = response.args.first.to_i

        if starz > 3
          response.reply "Wow! #{starz} stars!\nYou must be an internationally recognized expert!"
        else
          response.reply "You are not your star count! We <3 you!"
        end
      end

      Lita.register_handler(self)
    end
  end
end

stars.rb output

Lita > lita stars 2
You are not your star count! We <3 you!
Lita > lita stars 4
Wow! 4 stars!
You must be an internationally recognized expert!
:-D

Chat routes

restrict_to

module Lita
  module Handlers
    class Destroyer < Handler
      route(/destroy everything/, :destroy_it, command: true,
        restrict_to: [:authorized_destroyers])

      def destroy_it response
        response.reply "Sweet! Let's break stuff!"
      end

      Lita.register_handler(self)
    end
  end
end
For this work, you'll have to configure "Admins" in lita_config.rb. Then admins can add users to groups.

Authorization groups

lita_config.rb

Lita.configure do |config|

  # add adapter-specific ids
  config.robot.admins = ["42", "21"]

end

Authorization groups

Admins in action

Lita > lita auth add Michelle to authorized_destroyers
Michelle was added to authorized_destroyers
Lita > lita auth remove Michelle to authorized_destroyers
Michelle was removed to authorized_destroyers

http client

module Lita
  module Handlers
    class Nerdbeers < Handler
      route(/nerdbeers/i, :nerdbeers, command: true,
        help: { "nerdbeers" => "replies with current nerdbeers agenda" })

      def nerdbeers response
        http_response = http.get "http://nerdbeers.com/api/"
        agenda = MultiJson.load http_response.body

        message = []
        agenda['pairings'].each {|a| 
          message.push "Pairing ##{a['id']} => Topic: #{a['topic']} Beer: #{a['beer']}"
        }

        response.reply message.join "\n"
      end

      Lita.register_handler(self)
    end
  end
end
class var http is actually a Faraday::Connection. So anything you can do with Faraday, you can do here.

http client

In action...

Lita > lita nerdbeers
Pairing #1 => Topic: everything should be https Beer: coop f5
Pairing #2 => Topic: Google IO Beer: native amber
Pairing #3 => Topic: home automation in 2016 Beer: goose island ipa

HTTP routes

Supported verbs

* HEAD * GET * POST * PUT * PATCH * DELETE * OPTIONS * LINK * UNLINK Built-in HTTP server supports all the HTTP verbs

HTTP GET

module Lita
  module Handlers
    class UpstateHttp
      extend Lita::Handler::HTTPRouter

      http.get "/upstate", :ruby

      def ruby request, response
        response.body << "Ruby"
      end

      Lita.register_handler(UpstateHttp)
    end
  end
end
What kind of ChatOps discussion can this be with a "pinger" route? You don't have to inherit directly from Lita's Handler class. Notice this time we're extending HTTPRouter "Mixin" style.

GET /upstate

Went to the well, one more time!

HTTP GET

Advanced routing

module Lita
  module Handlers
    class StarsHttp < Handler
      http.get "/stars/:num", :star_rating

      def star_rating request, response
        starz = request.env["router.params"][:num].to_i
        message = starz < 4 ? "You are not your star count! We <3 you!" :
           "Wow! #{starz} stars!\nYou must be an internationally recognized expert!"

        body = { starz: starz, message: message }

        response.headers["Content-Type"] = "application/json"
        response.write MultiJson.dump(body)
      end

      Lita.register_handler(self)
    end
  end
end

GET /stars/4

Went to the well, one more time!

Wait there's more!

  • Event routes
  • Mixins
  • Helpers
  • Timers
  • Configuration
  • Templates
Event routes - handle or trigger events like "connected" Mixins - ChatRouter, EventRouter, HTTPRouter Helpers - robot, redis, http, translate, after/every, config, log Timers - Configuration - Templates - can use .erb for chat provider-specific rendering

Lita generators

lita handler

$ lita handler lita-openworks-status-handler
Do you want to test your plugin on Travis CI? ("yes" or "no", default is "no") no
Do you want to generate code coverage information with SimpleCov and Coveralls.io? ("yes" or "no", default is "no") no
      create  lita-openworks-status-handler/lib/lita/handlers/openworks_status_handler.rb
      create  lita-openworks-status-handler/lib/lita-openworks-status-handler.rb
      create  lita-openworks-status-handler/spec/lita/handlers/openworks_status_handler_spec.rb
      create  lita-openworks-status-handler/spec/spec_helper.rb
      create  lita-openworks-status-handler/locales/en.yml
      create  lita-openworks-status-handler/templates/.gitkeep
      create  lita-openworks-status-handler/Gemfile
      create  lita-openworks-status-handler/lita-openworks-status-handler.gemspec
      create  lita-openworks-status-handler/.gitignore
      create  lita-openworks-status-handler/Rakefile
      create  lita-openworks-status-handler/README.md
If you plan to release this plugin as open source software, consider adding a LICENSE file to the root of the repository.
Common open source software licenses can be found at http://choosealicense.com/.
 
Lita really encourages building handlers as RubyGems and testing with RSpec. Here's what the lita handler generator produces. Don't accidentally do this in your lita service project. :D

Resources

Lita

:-D

Resources

ChatOps

:-D

Thank You!

Intro to ChatOps with Lita

An Upstate Ruby lightning talk

Sample Scriptshttps://github.com/ryoe/upstate-ruby-demo

Comments or Questions?@ryoe_ok on Twitterryoe on GitHub