slacky 0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +4 -0
 - data/.ruby-version +1 -0
 - data/Gemfile +3 -0
 - data/Gemfile.lock +70 -0
 - data/README.md +15 -0
 - data/Rakefile +10 -0
 - data/lib/slacky.rb +12 -0
 - data/lib/slacky/bookkeeper.rb +55 -0
 - data/lib/slacky/bot.rb +229 -0
 - data/lib/slacky/channel.rb +103 -0
 - data/lib/slacky/cli.rb +35 -0
 - data/lib/slacky/config.rb +66 -0
 - data/lib/slacky/daemon.rb +67 -0
 - data/lib/slacky/message.rb +61 -0
 - data/lib/slacky/service.rb +73 -0
 - data/lib/slacky/user.rb +91 -0
 - data/lib/slacky/version.rb +3 -0
 - data/slacky.gemspec +31 -0
 - metadata +245 -0
 
    
        data/.gitignore
    ADDED
    
    
    
        data/.ruby-version
    ADDED
    
    | 
         @@ -0,0 +1 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            1.9.3-p448
         
     | 
    
        data/Gemfile
    ADDED
    
    
    
        data/Gemfile.lock
    ADDED
    
    | 
         @@ -0,0 +1,70 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            PATH
         
     | 
| 
      
 2 
     | 
    
         
            +
              remote: .
         
     | 
| 
      
 3 
     | 
    
         
            +
              specs:
         
     | 
| 
      
 4 
     | 
    
         
            +
                slacky (0.1)
         
     | 
| 
      
 5 
     | 
    
         
            +
                  dotenv
         
     | 
| 
      
 6 
     | 
    
         
            +
                  em-cron
         
     | 
| 
      
 7 
     | 
    
         
            +
                  eventmachine
         
     | 
| 
      
 8 
     | 
    
         
            +
                  faye-websocket
         
     | 
| 
      
 9 
     | 
    
         
            +
                  pg
         
     | 
| 
      
 10 
     | 
    
         
            +
                  slack-ruby-client (>= 0.6)
         
     | 
| 
      
 11 
     | 
    
         
            +
                  tzinfo
         
     | 
| 
      
 12 
     | 
    
         
            +
                  tzinfo-data
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
            GEM
         
     | 
| 
      
 15 
     | 
    
         
            +
              remote: http://rubygems.org/
         
     | 
| 
      
 16 
     | 
    
         
            +
              specs:
         
     | 
| 
      
 17 
     | 
    
         
            +
                activesupport (4.2.6)
         
     | 
| 
      
 18 
     | 
    
         
            +
                  i18n (~> 0.7)
         
     | 
| 
      
 19 
     | 
    
         
            +
                  json (~> 1.7, >= 1.7.7)
         
     | 
| 
      
 20 
     | 
    
         
            +
                  minitest (~> 5.1)
         
     | 
| 
      
 21 
     | 
    
         
            +
                  thread_safe (~> 0.3, >= 0.3.4)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  tzinfo (~> 1.1)
         
     | 
| 
      
 23 
     | 
    
         
            +
                dotenv (2.1.1)
         
     | 
| 
      
 24 
     | 
    
         
            +
                em-cron (0.2.0)
         
     | 
| 
      
 25 
     | 
    
         
            +
                  eventmachine
         
     | 
| 
      
 26 
     | 
    
         
            +
                  parse-cron (~> 0.1)
         
     | 
| 
      
 27 
     | 
    
         
            +
                eventmachine (1.2.0.1)
         
     | 
| 
      
 28 
     | 
    
         
            +
                factory_girl (4.7.0)
         
     | 
| 
      
 29 
     | 
    
         
            +
                  activesupport (>= 3.0.0)
         
     | 
| 
      
 30 
     | 
    
         
            +
                faraday (0.9.2)
         
     | 
| 
      
 31 
     | 
    
         
            +
                  multipart-post (>= 1.2, < 3)
         
     | 
| 
      
 32 
     | 
    
         
            +
                faraday_middleware (0.10.0)
         
     | 
| 
      
 33 
     | 
    
         
            +
                  faraday (>= 0.7.4, < 0.10)
         
     | 
| 
      
 34 
     | 
    
         
            +
                faye-websocket (0.10.3)
         
     | 
| 
      
 35 
     | 
    
         
            +
                  eventmachine (>= 0.12.0)
         
     | 
| 
      
 36 
     | 
    
         
            +
                  websocket-driver (>= 0.5.1)
         
     | 
| 
      
 37 
     | 
    
         
            +
                gli (2.13.4)
         
     | 
| 
      
 38 
     | 
    
         
            +
                hashie (3.4.4)
         
     | 
| 
      
 39 
     | 
    
         
            +
                i18n (0.7.0)
         
     | 
| 
      
 40 
     | 
    
         
            +
                json (1.8.3)
         
     | 
| 
      
 41 
     | 
    
         
            +
                minitest (5.8.4)
         
     | 
| 
      
 42 
     | 
    
         
            +
                multipart-post (2.0.0)
         
     | 
| 
      
 43 
     | 
    
         
            +
                parse-cron (0.1.4)
         
     | 
| 
      
 44 
     | 
    
         
            +
                pg (0.18.4)
         
     | 
| 
      
 45 
     | 
    
         
            +
                rake (11.1.2)
         
     | 
| 
      
 46 
     | 
    
         
            +
                slack-ruby-client (0.7.2)
         
     | 
| 
      
 47 
     | 
    
         
            +
                  activesupport
         
     | 
| 
      
 48 
     | 
    
         
            +
                  faraday
         
     | 
| 
      
 49 
     | 
    
         
            +
                  faraday_middleware
         
     | 
| 
      
 50 
     | 
    
         
            +
                  gli
         
     | 
| 
      
 51 
     | 
    
         
            +
                  hashie
         
     | 
| 
      
 52 
     | 
    
         
            +
                  json
         
     | 
| 
      
 53 
     | 
    
         
            +
                  websocket-driver
         
     | 
| 
      
 54 
     | 
    
         
            +
                thread_safe (0.3.5)
         
     | 
| 
      
 55 
     | 
    
         
            +
                tzinfo (1.2.2)
         
     | 
| 
      
 56 
     | 
    
         
            +
                  thread_safe (~> 0.1)
         
     | 
| 
      
 57 
     | 
    
         
            +
                tzinfo-data (1.2016.4)
         
     | 
| 
      
 58 
     | 
    
         
            +
                  tzinfo (>= 1.0.0)
         
     | 
| 
      
 59 
     | 
    
         
            +
                websocket-driver (0.6.3)
         
     | 
| 
      
 60 
     | 
    
         
            +
                  websocket-extensions (>= 0.1.0)
         
     | 
| 
      
 61 
     | 
    
         
            +
                websocket-extensions (0.1.2)
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
            PLATFORMS
         
     | 
| 
      
 64 
     | 
    
         
            +
              ruby
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
            DEPENDENCIES
         
     | 
| 
      
 67 
     | 
    
         
            +
              factory_girl
         
     | 
| 
      
 68 
     | 
    
         
            +
              minitest
         
     | 
| 
      
 69 
     | 
    
         
            +
              rake
         
     | 
| 
      
 70 
     | 
    
         
            +
              slacky!
         
     | 
    
        data/README.md
    ADDED
    
    | 
         @@ -0,0 +1,15 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # Slacky
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            ### Carbon Five Slack bot starter kit
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            This gem is an out of the box Slack bot running in Ruby.  It runs as a
         
     | 
| 
      
 6 
     | 
    
         
            +
            daemon, has a CLI, and in the future will integrate with other Carbon
         
     | 
| 
      
 7 
     | 
    
         
            +
            Five services (like Timesheet).
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            ## Installation
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            ...
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
            ## Usage
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            ...
         
     | 
    
        data/Rakefile
    ADDED
    
    
    
        data/lib/slacky.rb
    ADDED
    
    | 
         @@ -0,0 +1,12 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Slacky
         
     | 
| 
      
 2 
     | 
    
         
            +
              require 'slacky/version'
         
     | 
| 
      
 3 
     | 
    
         
            +
              require 'slacky/cli'
         
     | 
| 
      
 4 
     | 
    
         
            +
              require 'slacky/config'
         
     | 
| 
      
 5 
     | 
    
         
            +
              require 'slacky/daemon'
         
     | 
| 
      
 6 
     | 
    
         
            +
              require 'slacky/service'
         
     | 
| 
      
 7 
     | 
    
         
            +
              require 'slacky/bot'
         
     | 
| 
      
 8 
     | 
    
         
            +
              require 'slacky/bookkeeper'
         
     | 
| 
      
 9 
     | 
    
         
            +
              require 'slacky/user'
         
     | 
| 
      
 10 
     | 
    
         
            +
              require 'slacky/channel'
         
     | 
| 
      
 11 
     | 
    
         
            +
              require 'slacky/message'
         
     | 
| 
      
 12 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,55 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Slacky
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Bookkeeper
         
     | 
| 
      
 3 
     | 
    
         
            +
                def initialize(client)
         
     | 
| 
      
 4 
     | 
    
         
            +
                  @client = client
         
     | 
| 
      
 5 
     | 
    
         
            +
                end
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                def web_client
         
     | 
| 
      
 8 
     | 
    
         
            +
                  @client.web_client
         
     | 
| 
      
 9 
     | 
    
         
            +
                end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                def keep_the_books
         
     | 
| 
      
 12 
     | 
    
         
            +
                  @client.on :presence_change do |data|
         
     | 
| 
      
 13 
     | 
    
         
            +
                    next unless ( user = User.find data.user )
         
     | 
| 
      
 14 
     | 
    
         
            +
                    user.presence = data['presence']
         
     | 
| 
      
 15 
     | 
    
         
            +
                    user.save
         
     | 
| 
      
 16 
     | 
    
         
            +
                  end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                  @client.on :channel_created do |data|
         
     | 
| 
      
 19 
     | 
    
         
            +
                    web_client.channels_info(channel: data.channel.id).tap do |resp|
         
     | 
| 
      
 20 
     | 
    
         
            +
                      if resp.ok
         
     | 
| 
      
 21 
     | 
    
         
            +
                        channel = Channel.channel resp.channel
         
     | 
| 
      
 22 
     | 
    
         
            +
                        puts "Channel ##{channel.name}: channel_created"
         
     | 
| 
      
 23 
     | 
    
         
            +
                      end
         
     | 
| 
      
 24 
     | 
    
         
            +
                    end
         
     | 
| 
      
 25 
     | 
    
         
            +
                  end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                  handle_channel(:channel_deleted)   { |c| c.delete }
         
     | 
| 
      
 28 
     | 
    
         
            +
                  handle_channel(:channel_archive)   { |c| c.archive }
         
     | 
| 
      
 29 
     | 
    
         
            +
                  handle_channel(:channel_unarchive) { |c| c.unarchive }
         
     | 
| 
      
 30 
     | 
    
         
            +
                  handle_channel(:channel_rename)    { |c, data| c.rename data.channel.name }
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                  @client.on :group_joined do |data|
         
     | 
| 
      
 33 
     | 
    
         
            +
                    next unless data.channel.is_group
         
     | 
| 
      
 34 
     | 
    
         
            +
                    channel = Channel.group data.channel
         
     | 
| 
      
 35 
     | 
    
         
            +
                    puts "Channel ##{channel.name}: group_joined"
         
     | 
| 
      
 36 
     | 
    
         
            +
                  end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                  handle_channel(:group_left)        { |g| g.delete }
         
     | 
| 
      
 39 
     | 
    
         
            +
                  handle_channel(:group_archive)     { |g| g.archive }
         
     | 
| 
      
 40 
     | 
    
         
            +
                  handle_channel(:group_unarchive)   { |g| g.unarchive }
         
     | 
| 
      
 41 
     | 
    
         
            +
                  handle_channel(:group_rename)      { |g, data| g.rename data.channel.name }
         
     | 
| 
      
 42 
     | 
    
         
            +
                end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                def handle_channel(event)
         
     | 
| 
      
 45 
     | 
    
         
            +
                  @client.on event do |data|
         
     | 
| 
      
 46 
     | 
    
         
            +
                    channel_id = data.channel.is_a?(String) ? data.channel : data.channel.id
         
     | 
| 
      
 47 
     | 
    
         
            +
                    Channel.find(channel_id).tap do |channel|
         
     | 
| 
      
 48 
     | 
    
         
            +
                      yield channel, data
         
     | 
| 
      
 49 
     | 
    
         
            +
                      puts "Channel ##{channel.name}: #{event}"
         
     | 
| 
      
 50 
     | 
    
         
            +
                    end
         
     | 
| 
      
 51 
     | 
    
         
            +
                  end
         
     | 
| 
      
 52 
     | 
    
         
            +
                end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
              end
         
     | 
| 
      
 55 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/slacky/bot.rb
    ADDED
    
    | 
         @@ -0,0 +1,229 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'slack-ruby-client'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'set'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'tzinfo'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'em/cron'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            module Slacky
         
     | 
| 
      
 7 
     | 
    
         
            +
              class Bot
         
     | 
| 
      
 8 
     | 
    
         
            +
                attr_reader :client, :config, :slack_id
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                def initialize(config)
         
     | 
| 
      
 11 
     | 
    
         
            +
                  puts "#{config.name} is starting up..."
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                  @config = config
         
     | 
| 
      
 14 
     | 
    
         
            +
                  @restarts = []
         
     | 
| 
      
 15 
     | 
    
         
            +
                  @command_handlers = []
         
     | 
| 
      
 16 
     | 
    
         
            +
                  @channel_handlers = []
         
     | 
| 
      
 17 
     | 
    
         
            +
                  @im_handlers = []
         
     | 
| 
      
 18 
     | 
    
         
            +
                  @raw_handlers = []
         
     | 
| 
      
 19 
     | 
    
         
            +
                  @cron_handlers = []
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                  unless @config.slack_api_token
         
     | 
| 
      
 22 
     | 
    
         
            +
                    @config.log "No Slack API token found.  Use environment variable SLACK_API_TOKEN."
         
     | 
| 
      
 23 
     | 
    
         
            +
                    return
         
     | 
| 
      
 24 
     | 
    
         
            +
                  end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                  Slack.configure do |slack_cfg|
         
     | 
| 
      
 27 
     | 
    
         
            +
                    slack_cfg.token = @config.slack_api_token
         
     | 
| 
      
 28 
     | 
    
         
            +
                  end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                  @client = Slack::RealTime::Client.new
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                  auth = web_client.auth_test
         
     | 
| 
      
 33 
     | 
    
         
            +
                  if auth.ok
         
     | 
| 
      
 34 
     | 
    
         
            +
                    @slack_id = auth.user_id
         
     | 
| 
      
 35 
     | 
    
         
            +
                    @config.log "Slackbot is active!"
         
     | 
| 
      
 36 
     | 
    
         
            +
                  else
         
     | 
| 
      
 37 
     | 
    
         
            +
                    @config.log "Slackbot is doomed :-("
         
     | 
| 
      
 38 
     | 
    
         
            +
                    return
         
     | 
| 
      
 39 
     | 
    
         
            +
                  end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                  @bookkeeper = Bookkeeper.new @client
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                  Channel.bot = self
         
     | 
| 
      
 44 
     | 
    
         
            +
                  Message.bot = self
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                  populate_users
         
     | 
| 
      
 47 
     | 
    
         
            +
                  populate_channels
         
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                def web_client
         
     | 
| 
      
 51 
     | 
    
         
            +
                  @client.web_client
         
     | 
| 
      
 52 
     | 
    
         
            +
                end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                def name
         
     | 
| 
      
 55 
     | 
    
         
            +
                  @config.down_name
         
     | 
| 
      
 56 
     | 
    
         
            +
                end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                def known_commands
         
     | 
| 
      
 59 
     | 
    
         
            +
                  @command_handlers.map { |ch| ch[:command] }
         
     | 
| 
      
 60 
     | 
    
         
            +
                end
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                def on(type, &block)
         
     | 
| 
      
 63 
     | 
    
         
            +
                  @raw_handlers << { type: type, handler: block }
         
     | 
| 
      
 64 
     | 
    
         
            +
                end
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                def on_command(command, &block)
         
     | 
| 
      
 67 
     | 
    
         
            +
                  @command_handlers << { command: command.downcase, handler: block }
         
     | 
| 
      
 68 
     | 
    
         
            +
                end
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                def on_message(attrs, &block)
         
     | 
| 
      
 71 
     | 
    
         
            +
                  attrs ||= {}
         
     | 
| 
      
 72 
     | 
    
         
            +
                  @channel_handlers << { match: attrs[:match], channels: attrs[:channels], handler: block }
         
     | 
| 
      
 73 
     | 
    
         
            +
                end
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                def on_im(attrs, &block)
         
     | 
| 
      
 76 
     | 
    
         
            +
                  attrs ||= {}
         
     | 
| 
      
 77 
     | 
    
         
            +
                  @im_handlers << { match: attrs[:match], handler: block }
         
     | 
| 
      
 78 
     | 
    
         
            +
                end
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                def at(cron, &block)
         
     | 
| 
      
 81 
     | 
    
         
            +
                  @cron_handlers << { cron: cron, handler: block }
         
     | 
| 
      
 82 
     | 
    
         
            +
                end
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                def handle_channel(message)
         
     | 
| 
      
 85 
     | 
    
         
            +
                  handled = false
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
                  if message.command?
         
     | 
| 
      
 88 
     | 
    
         
            +
                    @command_handlers.each do |h|
         
     | 
| 
      
 89 
     | 
    
         
            +
                      command, handler = h.values_at :command, :handler
         
     | 
| 
      
 90 
     | 
    
         
            +
                      next unless command == message.command
         
     | 
| 
      
 91 
     | 
    
         
            +
                      @client.typing channel: message.channel.slack_id
         
     | 
| 
      
 92 
     | 
    
         
            +
                      handler.call message
         
     | 
| 
      
 93 
     | 
    
         
            +
                      handled = true
         
     | 
| 
      
 94 
     | 
    
         
            +
                    end
         
     | 
| 
      
 95 
     | 
    
         
            +
                  end
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
                  return if handled
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
                  @channel_handlers.each do |h|
         
     | 
| 
      
 100 
     | 
    
         
            +
                    match, channels, handler = h.values_at :match, :channels, :handler
         
     | 
| 
      
 101 
     | 
    
         
            +
                    accept = Channel.find channels
         
     | 
| 
      
 102 
     | 
    
         
            +
                    next if accept && accept.include?(message.channel)
         
     | 
| 
      
 103 
     | 
    
         
            +
                    next if match && ! match === message
         
     | 
| 
      
 104 
     | 
    
         
            +
                    @client.typing channel: message.channel.slack_id
         
     | 
| 
      
 105 
     | 
    
         
            +
                    handler.call message
         
     | 
| 
      
 106 
     | 
    
         
            +
                  end
         
     | 
| 
      
 107 
     | 
    
         
            +
                end
         
     | 
| 
      
 108 
     | 
    
         
            +
             
     | 
| 
      
 109 
     | 
    
         
            +
                def handle_im(message)
         
     | 
| 
      
 110 
     | 
    
         
            +
                  unless message.user.slack_im_id == message.channel.slack_id
         
     | 
| 
      
 111 
     | 
    
         
            +
                    message.user.slack_im_id = message.channel.slack_id
         
     | 
| 
      
 112 
     | 
    
         
            +
                    message.user.save
         
     | 
| 
      
 113 
     | 
    
         
            +
                  end
         
     | 
| 
      
 114 
     | 
    
         
            +
             
     | 
| 
      
 115 
     | 
    
         
            +
                  handled = false
         
     | 
| 
      
 116 
     | 
    
         
            +
             
     | 
| 
      
 117 
     | 
    
         
            +
                  @command_handlers.each do |h|
         
     | 
| 
      
 118 
     | 
    
         
            +
                    command, handler = h.values_at :command, :handler
         
     | 
| 
      
 119 
     | 
    
         
            +
                    next unless command == message.command
         
     | 
| 
      
 120 
     | 
    
         
            +
                    @client.typing channel: message.channel.slack_id
         
     | 
| 
      
 121 
     | 
    
         
            +
                    handler.call message
         
     | 
| 
      
 122 
     | 
    
         
            +
                    handled = true
         
     | 
| 
      
 123 
     | 
    
         
            +
                  end
         
     | 
| 
      
 124 
     | 
    
         
            +
             
     | 
| 
      
 125 
     | 
    
         
            +
                  return if handled
         
     | 
| 
      
 126 
     | 
    
         
            +
             
     | 
| 
      
 127 
     | 
    
         
            +
                  @im_handlers.each do |h|
         
     | 
| 
      
 128 
     | 
    
         
            +
                    match, handler = h.values_at :match, :handler
         
     | 
| 
      
 129 
     | 
    
         
            +
                    next if match && ! match === message
         
     | 
| 
      
 130 
     | 
    
         
            +
                    @client.typing channel: message.channel.slack_id
         
     | 
| 
      
 131 
     | 
    
         
            +
                    handler.call message
         
     | 
| 
      
 132 
     | 
    
         
            +
                  end
         
     | 
| 
      
 133 
     | 
    
         
            +
                end
         
     | 
| 
      
 134 
     | 
    
         
            +
             
     | 
| 
      
 135 
     | 
    
         
            +
                def run
         
     | 
| 
      
 136 
     | 
    
         
            +
                  @bookkeeper.keep_the_books
         
     | 
| 
      
 137 
     | 
    
         
            +
             
     | 
| 
      
 138 
     | 
    
         
            +
                  @client.on :message do |data|
         
     | 
| 
      
 139 
     | 
    
         
            +
                    next unless ( user = User.find data.user )
         
     | 
| 
      
 140 
     | 
    
         
            +
             
     | 
| 
      
 141 
     | 
    
         
            +
                    channel = Channel.find data.channel
         
     | 
| 
      
 142 
     | 
    
         
            +
                    channel = Channel.im data.channel, user if data.channel =~ /^D/ && ! channel
         
     | 
| 
      
 143 
     | 
    
         
            +
                    next unless channel
         
     | 
| 
      
 144 
     | 
    
         
            +
             
     | 
| 
      
 145 
     | 
    
         
            +
                    reject = Channel.find @config.slack_reject_channels
         
     | 
| 
      
 146 
     | 
    
         
            +
                    next if reject && reject.find { |c| c.slack_id == data.channel }
         
     | 
| 
      
 147 
     | 
    
         
            +
             
     | 
| 
      
 148 
     | 
    
         
            +
                    accept = Channel.find @config.slack_accept_channels
         
     | 
| 
      
 149 
     | 
    
         
            +
                    next if accept && ! accept.find { |c| c.slack_id == data.channel }
         
     | 
| 
      
 150 
     | 
    
         
            +
             
     | 
| 
      
 151 
     | 
    
         
            +
                    message = Message.new(user, channel, data)
         
     | 
| 
      
 152 
     | 
    
         
            +
                    handle_channel message if [ :group, :channel ].include? channel.type
         
     | 
| 
      
 153 
     | 
    
         
            +
                    handle_im      message if [ :im              ].include? channel.type
         
     | 
| 
      
 154 
     | 
    
         
            +
                  end
         
     | 
| 
      
 155 
     | 
    
         
            +
             
     | 
| 
      
 156 
     | 
    
         
            +
                  @raw_handlers.each do |h|
         
     | 
| 
      
 157 
     | 
    
         
            +
                    type, handler = h.values_at :type, :handler
         
     | 
| 
      
 158 
     | 
    
         
            +
                    @client.on type do |data|
         
     | 
| 
      
 159 
     | 
    
         
            +
                      handler.call data
         
     | 
| 
      
 160 
     | 
    
         
            +
                    end
         
     | 
| 
      
 161 
     | 
    
         
            +
                  end
         
     | 
| 
      
 162 
     | 
    
         
            +
             
     | 
| 
      
 163 
     | 
    
         
            +
                  @cron_thread ||= Thread.new do
         
     | 
| 
      
 164 
     | 
    
         
            +
                    EM.run do
         
     | 
| 
      
 165 
     | 
    
         
            +
                      @cron_handlers.each do |h|
         
     | 
| 
      
 166 
     | 
    
         
            +
                        cron, handler = h.values_at :cron, :handler
         
     | 
| 
      
 167 
     | 
    
         
            +
                        EM::Cron.schedule cron do |time|
         
     | 
| 
      
 168 
     | 
    
         
            +
                          begin
         
     | 
| 
      
 169 
     | 
    
         
            +
                            handler.call
         
     | 
| 
      
 170 
     | 
    
         
            +
                          rescue => e
         
     | 
| 
      
 171 
     | 
    
         
            +
                            @config.log "An error ocurred inside the Slackbot (in a scheduled block)", e
         
     | 
| 
      
 172 
     | 
    
         
            +
                          end
         
     | 
| 
      
 173 
     | 
    
         
            +
                        end
         
     | 
| 
      
 174 
     | 
    
         
            +
                      end
         
     | 
| 
      
 175 
     | 
    
         
            +
                    end
         
     | 
| 
      
 176 
     | 
    
         
            +
                  end
         
     | 
| 
      
 177 
     | 
    
         
            +
             
     | 
| 
      
 178 
     | 
    
         
            +
                  puts "#{@config.name} is listening to: #{@config.slack_accept_channels}"
         
     | 
| 
      
 179 
     | 
    
         
            +
             
     | 
| 
      
 180 
     | 
    
         
            +
                  @client.start!
         
     | 
| 
      
 181 
     | 
    
         
            +
                rescue => e
         
     | 
| 
      
 182 
     | 
    
         
            +
                  @config.log "An error ocurred inside the Slackbot", e
         
     | 
| 
      
 183 
     | 
    
         
            +
                  @restarts << Time.new
         
     | 
| 
      
 184 
     | 
    
         
            +
                  @restarts.shift while (@restarts.length > 3)
         
     | 
| 
      
 185 
     | 
    
         
            +
                  if @restarts.length == 3 and ( Time.new - @restarts.first < 30 )
         
     | 
| 
      
 186 
     | 
    
         
            +
                    @config.log "Too many errors.  Not restarting anymore."
         
     | 
| 
      
 187 
     | 
    
         
            +
                  else
         
     | 
| 
      
 188 
     | 
    
         
            +
                    run
         
     | 
| 
      
 189 
     | 
    
         
            +
                  end
         
     | 
| 
      
 190 
     | 
    
         
            +
                end
         
     | 
| 
      
 191 
     | 
    
         
            +
             
     | 
| 
      
 192 
     | 
    
         
            +
                def populate_users
         
     | 
| 
      
 193 
     | 
    
         
            +
                  print "Getting users from Slack..."
         
     | 
| 
      
 194 
     | 
    
         
            +
                  resp = web_client.users_list presence: 1
         
     | 
| 
      
 195 
     | 
    
         
            +
                  throw resp unless resp.ok
         
     | 
| 
      
 196 
     | 
    
         
            +
                  resp.members.map do |member|
         
     | 
| 
      
 197 
     | 
    
         
            +
                    next unless member.profile.email # no bots
         
     | 
| 
      
 198 
     | 
    
         
            +
                    next if member.deleted # no ghosts
         
     | 
| 
      
 199 
     | 
    
         
            +
                    next if member.is_ultra_restricted # no single channel guests
         
     | 
| 
      
 200 
     | 
    
         
            +
                    user = User.find(member.id) || User.new(slack_id: member.id)
         
     | 
| 
      
 201 
     | 
    
         
            +
                    user.populate(member).save
         
     | 
| 
      
 202 
     | 
    
         
            +
                  end
         
     | 
| 
      
 203 
     | 
    
         
            +
                  puts " done!"
         
     | 
| 
      
 204 
     | 
    
         
            +
                end
         
     | 
| 
      
 205 
     | 
    
         
            +
             
     | 
| 
      
 206 
     | 
    
         
            +
                def populate_channels
         
     | 
| 
      
 207 
     | 
    
         
            +
                  print "Getting channels from Slack..."
         
     | 
| 
      
 208 
     | 
    
         
            +
                  resp = web_client.channels_list
         
     | 
| 
      
 209 
     | 
    
         
            +
                  throw resp unless resp.ok
         
     | 
| 
      
 210 
     | 
    
         
            +
                  resp.channels.map do |channel|
         
     | 
| 
      
 211 
     | 
    
         
            +
                    Channel.channel channel
         
     | 
| 
      
 212 
     | 
    
         
            +
                  end
         
     | 
| 
      
 213 
     | 
    
         
            +
             
     | 
| 
      
 214 
     | 
    
         
            +
                  resp = web_client.groups_list
         
     | 
| 
      
 215 
     | 
    
         
            +
                  throw resp unless resp.ok
         
     | 
| 
      
 216 
     | 
    
         
            +
                  resp.groups.map do |group|
         
     | 
| 
      
 217 
     | 
    
         
            +
                    Channel.group group
         
     | 
| 
      
 218 
     | 
    
         
            +
                  end
         
     | 
| 
      
 219 
     | 
    
         
            +
                  puts " done!"
         
     | 
| 
      
 220 
     | 
    
         
            +
                end
         
     | 
| 
      
 221 
     | 
    
         
            +
             
     | 
| 
      
 222 
     | 
    
         
            +
                def blowup(user, data, args, &respond)
         
     | 
| 
      
 223 
     | 
    
         
            +
                  respond.call "Tick... tick... tick... BOOM!   Goodbye."
         
     | 
| 
      
 224 
     | 
    
         
            +
                  EM.next_tick do
         
     | 
| 
      
 225 
     | 
    
         
            +
                    raise "kablammo!"
         
     | 
| 
      
 226 
     | 
    
         
            +
                  end
         
     | 
| 
      
 227 
     | 
    
         
            +
                end
         
     | 
| 
      
 228 
     | 
    
         
            +
              end
         
     | 
| 
      
 229 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,103 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Slacky
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Channel
         
     | 
| 
      
 3 
     | 
    
         
            +
                attr_reader :slack_id, :topic, :purpose, :members, :type
         
     | 
| 
      
 4 
     | 
    
         
            +
                attr_accessor :name
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                @@channels = {}
         
     | 
| 
      
 7 
     | 
    
         
            +
                @@bot = nil
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                def self.bot=(bot)
         
     | 
| 
      
 10 
     | 
    
         
            +
                  @@bot = bot
         
     | 
| 
      
 11 
     | 
    
         
            +
                end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                def self.find(channel)
         
     | 
| 
      
 14 
     | 
    
         
            +
                  return nil unless channel
         
     | 
| 
      
 15 
     | 
    
         
            +
                  return channel.map { |c| Channel.find c }.compact if channel.is_a? Array
         
     | 
| 
      
 16 
     | 
    
         
            +
                  return channel if channel.is_a? Channel
         
     | 
| 
      
 17 
     | 
    
         
            +
                  @@channels[channel]
         
     | 
| 
      
 18 
     | 
    
         
            +
                end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                def self.im(channel_id, user)
         
     | 
| 
      
 21 
     | 
    
         
            +
                  Channel.new.populate_im(channel_id, user).save
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                def self.channel(channel_data)
         
     | 
| 
      
 25 
     | 
    
         
            +
                  Channel.new.populate_channel(channel_data).save
         
     | 
| 
      
 26 
     | 
    
         
            +
                end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                def self.group(group_data)
         
     | 
| 
      
 29 
     | 
    
         
            +
                  Channel.new.populate_group(group_data).save
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                def archived?
         
     | 
| 
      
 33 
     | 
    
         
            +
                  @archived
         
     | 
| 
      
 34 
     | 
    
         
            +
                end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                def archive
         
     | 
| 
      
 37 
     | 
    
         
            +
                  @archived = true
         
     | 
| 
      
 38 
     | 
    
         
            +
                end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                def unarchive
         
     | 
| 
      
 41 
     | 
    
         
            +
                  @archived = false
         
     | 
| 
      
 42 
     | 
    
         
            +
                end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                def delete
         
     | 
| 
      
 45 
     | 
    
         
            +
                  @@channels.delete @slack_id
         
     | 
| 
      
 46 
     | 
    
         
            +
                  @@channels.delete "##{@name}"
         
     | 
| 
      
 47 
     | 
    
         
            +
                  @@channels.delete "@#{@user.username}" if @user
         
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                def rename(name)
         
     | 
| 
      
 51 
     | 
    
         
            +
                  @@channels.delete "##{@name}"
         
     | 
| 
      
 52 
     | 
    
         
            +
                  @name = name
         
     | 
| 
      
 53 
     | 
    
         
            +
                  @@channels["##{@name}"] = self
         
     | 
| 
      
 54 
     | 
    
         
            +
                end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                def member?
         
     | 
| 
      
 57 
     | 
    
         
            +
                  case @type
         
     | 
| 
      
 58 
     | 
    
         
            +
                  when :channel ; @member
         
     | 
| 
      
 59 
     | 
    
         
            +
                  when :group   ; @members[@@bot.slack_id]
         
     | 
| 
      
 60 
     | 
    
         
            +
                  when :im      ; true
         
     | 
| 
      
 61 
     | 
    
         
            +
                  else            throw "Unknown channel type: #{@type}"
         
     | 
| 
      
 62 
     | 
    
         
            +
                  end
         
     | 
| 
      
 63 
     | 
    
         
            +
                end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                def save
         
     | 
| 
      
 66 
     | 
    
         
            +
                  @@channels[@slack_id] = self
         
     | 
| 
      
 67 
     | 
    
         
            +
                  @@channels["##{@name}"] = self if @name
         
     | 
| 
      
 68 
     | 
    
         
            +
                  @@channels["@#{@user.username}"] = self if @user
         
     | 
| 
      
 69 
     | 
    
         
            +
                  self
         
     | 
| 
      
 70 
     | 
    
         
            +
                end
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                def populate_channel(channel)
         
     | 
| 
      
 73 
     | 
    
         
            +
                  populate channel
         
     | 
| 
      
 74 
     | 
    
         
            +
                  @type   = :channel
         
     | 
| 
      
 75 
     | 
    
         
            +
                  @member = channel.is_member
         
     | 
| 
      
 76 
     | 
    
         
            +
                  self
         
     | 
| 
      
 77 
     | 
    
         
            +
                end
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                def populate_group(group)
         
     | 
| 
      
 80 
     | 
    
         
            +
                  populate group
         
     | 
| 
      
 81 
     | 
    
         
            +
                  @type     = :group
         
     | 
| 
      
 82 
     | 
    
         
            +
                  @members  = User.find group.members
         
     | 
| 
      
 83 
     | 
    
         
            +
                  self
         
     | 
| 
      
 84 
     | 
    
         
            +
                end
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
                def populate_im(slack_id, user)
         
     | 
| 
      
 87 
     | 
    
         
            +
                  @type     = :im
         
     | 
| 
      
 88 
     | 
    
         
            +
                  @slack_id = slack_id
         
     | 
| 
      
 89 
     | 
    
         
            +
                  @user     = user
         
     | 
| 
      
 90 
     | 
    
         
            +
                  self
         
     | 
| 
      
 91 
     | 
    
         
            +
                end
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
                private
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
      
 95 
     | 
    
         
            +
                def populate(channel)
         
     | 
| 
      
 96 
     | 
    
         
            +
                  @slack_id = channel.id
         
     | 
| 
      
 97 
     | 
    
         
            +
                  @name     = channel.name
         
     | 
| 
      
 98 
     | 
    
         
            +
                  @archived = channel.is_archived
         
     | 
| 
      
 99 
     | 
    
         
            +
                  @topic    = channel.topic.value
         
     | 
| 
      
 100 
     | 
    
         
            +
                  @purpose  = channel.purpose.value
         
     | 
| 
      
 101 
     | 
    
         
            +
                end
         
     | 
| 
      
 102 
     | 
    
         
            +
              end
         
     | 
| 
      
 103 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/slacky/cli.rb
    ADDED
    
    | 
         @@ -0,0 +1,35 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Slacky
         
     | 
| 
      
 2 
     | 
    
         
            +
              class CLI
         
     | 
| 
      
 3 
     | 
    
         
            +
                attr_reader :bot
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
                def initialize(name, opts)
         
     | 
| 
      
 6 
     | 
    
         
            +
                  throw "CLI must be passed a name" unless name
         
     | 
| 
      
 7 
     | 
    
         
            +
                  @options = { :verbose => false }.merge opts
         
     | 
| 
      
 8 
     | 
    
         
            +
                  config = Config.new name
         
     | 
| 
      
 9 
     | 
    
         
            +
                  @bot = Bot.new config
         
     | 
| 
      
 10 
     | 
    
         
            +
                  daemon = Daemon.new config, bot
         
     | 
| 
      
 11 
     | 
    
         
            +
                  @service = Service.new config, daemon
         
     | 
| 
      
 12 
     | 
    
         
            +
                end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                def run(params)
         
     | 
| 
      
 15 
     | 
    
         
            +
                  @service.run
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                def start(params)
         
     | 
| 
      
 19 
     | 
    
         
            +
                  @service.start
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                def stop(params)
         
     | 
| 
      
 23 
     | 
    
         
            +
                  @service.stop
         
     | 
| 
      
 24 
     | 
    
         
            +
                end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                def restart(params)
         
     | 
| 
      
 27 
     | 
    
         
            +
                  @service.restart
         
     | 
| 
      
 28 
     | 
    
         
            +
                end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                def status(params)
         
     | 
| 
      
 31 
     | 
    
         
            +
                  @service.status
         
     | 
| 
      
 32 
     | 
    
         
            +
                end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
              end
         
     | 
| 
      
 35 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,66 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'yaml'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'time'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'pg'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'dotenv'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            module Slacky
         
     | 
| 
      
 7 
     | 
    
         
            +
              class Config
         
     | 
| 
      
 8 
     | 
    
         
            +
                attr_reader :pid_file, :name, :db
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                def initialize(name, opts = {})
         
     | 
| 
      
 11 
     | 
    
         
            +
                  @name = name
         
     | 
| 
      
 12 
     | 
    
         
            +
                  Dotenv.load ".env", "#{config_dir}/.env"
         
     | 
| 
      
 13 
     | 
    
         
            +
                  FileUtils.mkdir config_dir unless File.directory? config_dir
         
     | 
| 
      
 14 
     | 
    
         
            +
                  @pid_file = "#{config_dir}/pid"
         
     | 
| 
      
 15 
     | 
    
         
            +
                  @db = PG.connect(db_connect_params)
         
     | 
| 
      
 16 
     | 
    
         
            +
                  User.db = @db
         
     | 
| 
      
 17 
     | 
    
         
            +
                  User.initialize_table
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                  @timestamps = {}
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                def down_name
         
     | 
| 
      
 23 
     | 
    
         
            +
                  @name.downcase
         
     | 
| 
      
 24 
     | 
    
         
            +
                end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                def slack_api_token
         
     | 
| 
      
 27 
     | 
    
         
            +
                  ENV['SLACK_API_TOKEN']
         
     | 
| 
      
 28 
     | 
    
         
            +
                end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                def config_dir
         
     | 
| 
      
 31 
     | 
    
         
            +
                  ENV['CONFIG_DIR'] || "#{ENV['HOME']}/.#{down_name}"
         
     | 
| 
      
 32 
     | 
    
         
            +
                end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                def slack_reject_channels
         
     | 
| 
      
 35 
     | 
    
         
            +
                  return nil unless ENV['REJECT_CHANNELS']
         
     | 
| 
      
 36 
     | 
    
         
            +
                  ENV['REJECT_CHANNELS'].split(',').map {|c| c.strip}
         
     | 
| 
      
 37 
     | 
    
         
            +
                end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                def slack_accept_channels
         
     | 
| 
      
 40 
     | 
    
         
            +
                  return nil unless ENV['ACCEPT_CHANNELS']
         
     | 
| 
      
 41 
     | 
    
         
            +
                  ENV['ACCEPT_CHANNELS'].split(',').map {|c| c.strip}
         
     | 
| 
      
 42 
     | 
    
         
            +
                end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                def log(msg, ex = nil)
         
     | 
| 
      
 45 
     | 
    
         
            +
                  log = File.new(log_file, 'a')
         
     | 
| 
      
 46 
     | 
    
         
            +
                  timestamp = Time.now.strftime('%Y-%m-%d %H:%M:%S')
         
     | 
| 
      
 47 
     | 
    
         
            +
                  type = ex ? 'ERROR' : ' INFO'
         
     | 
| 
      
 48 
     | 
    
         
            +
                  log.puts "#{type}  #{timestamp}  #{msg}"
         
     | 
| 
      
 49 
     | 
    
         
            +
                  if ex
         
     | 
| 
      
 50 
     | 
    
         
            +
                    log.puts ex.message
         
     | 
| 
      
 51 
     | 
    
         
            +
                    log.puts("Stacktrace:\n" + ex.backtrace.join("\n"))
         
     | 
| 
      
 52 
     | 
    
         
            +
                  end
         
     | 
| 
      
 53 
     | 
    
         
            +
                  log.flush
         
     | 
| 
      
 54 
     | 
    
         
            +
                end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                private
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                def db_connect_params
         
     | 
| 
      
 59 
     | 
    
         
            +
                  ENV['DATABASE_URL'] || { dbname: "slacky_#{down_name}" }
         
     | 
| 
      
 60 
     | 
    
         
            +
                end
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                def log_file
         
     | 
| 
      
 63 
     | 
    
         
            +
                  "#{config_dir}/#{down_name}.log"
         
     | 
| 
      
 64 
     | 
    
         
            +
                end
         
     | 
| 
      
 65 
     | 
    
         
            +
              end
         
     | 
| 
      
 66 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,67 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Slacky
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Daemon
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
                def initialize(config, bot)
         
     | 
| 
      
 5 
     | 
    
         
            +
                  @config = config
         
     | 
| 
      
 6 
     | 
    
         
            +
                  @bot = bot
         
     | 
| 
      
 7 
     | 
    
         
            +
                  @active = true
         
     | 
| 
      
 8 
     | 
    
         
            +
                  @running = false
         
     | 
| 
      
 9 
     | 
    
         
            +
                end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                def start(daemonize = true)
         
     | 
| 
      
 12 
     | 
    
         
            +
                  Process.daemon if daemonize
         
     | 
| 
      
 13 
     | 
    
         
            +
                  write_pid
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                  [ 'HUP', 'INT', 'QUIT', 'TERM' ].each do |sig|
         
     | 
| 
      
 16 
     | 
    
         
            +
                    Signal.trap(sig) do
         
     | 
| 
      
 17 
     | 
    
         
            +
                      @config.log "Interrupted with signal: #{sig}"
         
     | 
| 
      
 18 
     | 
    
         
            +
                      kill
         
     | 
| 
      
 19 
     | 
    
         
            +
                    end
         
     | 
| 
      
 20 
     | 
    
         
            +
                  end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                  begin
         
     | 
| 
      
 23 
     | 
    
         
            +
                    @slackthread = Thread.new { @bot.run }
         
     | 
| 
      
 24 
     | 
    
         
            +
                    run @bot
         
     | 
| 
      
 25 
     | 
    
         
            +
                  rescue => e
         
     | 
| 
      
 26 
     | 
    
         
            +
                    @config.log "Unexpected error", e
         
     | 
| 
      
 27 
     | 
    
         
            +
                  ensure
         
     | 
| 
      
 28 
     | 
    
         
            +
                    cleanup
         
     | 
| 
      
 29 
     | 
    
         
            +
                  end
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                def cleanup
         
     | 
| 
      
 33 
     | 
    
         
            +
                  delete_pid
         
     | 
| 
      
 34 
     | 
    
         
            +
                end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                private
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                def run(slackbot)
         
     | 
| 
      
 39 
     | 
    
         
            +
                  @config.log "#{@config.name} is running."
         
     | 
| 
      
 40 
     | 
    
         
            +
                  while active? do
         
     | 
| 
      
 41 
     | 
    
         
            +
                    # TODO: handle timed tasks
         
     | 
| 
      
 42 
     | 
    
         
            +
                    #slackbot.ask_all if time.min % 10 == 0  # every 10 minutes
         
     | 
| 
      
 43 
     | 
    
         
            +
                    sleep 0.5
         
     | 
| 
      
 44 
     | 
    
         
            +
                  end
         
     | 
| 
      
 45 
     | 
    
         
            +
                  @config.log "#{@config.name} got killed"
         
     | 
| 
      
 46 
     | 
    
         
            +
                  @slackthread.kill
         
     | 
| 
      
 47 
     | 
    
         
            +
                end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                def active?
         
     | 
| 
      
 50 
     | 
    
         
            +
                  @active
         
     | 
| 
      
 51 
     | 
    
         
            +
                end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                def kill
         
     | 
| 
      
 54 
     | 
    
         
            +
                  @active = false
         
     | 
| 
      
 55 
     | 
    
         
            +
                end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                def write_pid
         
     | 
| 
      
 58 
     | 
    
         
            +
                  File.open @config.pid_file, 'w' do |f|
         
     | 
| 
      
 59 
     | 
    
         
            +
                    f.write Process.pid.to_s
         
     | 
| 
      
 60 
     | 
    
         
            +
                  end
         
     | 
| 
      
 61 
     | 
    
         
            +
                end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                def delete_pid
         
     | 
| 
      
 64 
     | 
    
         
            +
                  File.delete @config.pid_file if File.exists? @config.pid_file
         
     | 
| 
      
 65 
     | 
    
         
            +
                end
         
     | 
| 
      
 66 
     | 
    
         
            +
              end
         
     | 
| 
      
 67 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,61 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Slacky
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Message
         
     | 
| 
      
 3 
     | 
    
         
            +
                attr_reader :raw, :text, :user, :channel
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
                @@decorator = @@bot = nil
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                def self.decorator=(decorator)
         
     | 
| 
      
 8 
     | 
    
         
            +
                  @@decorator = decorator
         
     | 
| 
      
 9 
     | 
    
         
            +
                end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                def self.bot=(bot)
         
     | 
| 
      
 12 
     | 
    
         
            +
                  @@bot = bot
         
     | 
| 
      
 13 
     | 
    
         
            +
                end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                def initialize(user, channel, raw)
         
     | 
| 
      
 16 
     | 
    
         
            +
                  @raw = raw
         
     | 
| 
      
 17 
     | 
    
         
            +
                  @user = user
         
     | 
| 
      
 18 
     | 
    
         
            +
                  @channel = channel
         
     | 
| 
      
 19 
     | 
    
         
            +
                  @text = raw.text.strip
         
     | 
| 
      
 20 
     | 
    
         
            +
                  @pieces = @text.split ' '
         
     | 
| 
      
 21 
     | 
    
         
            +
                  self.extend @@decorator if @@decorator
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                def reply(msg)
         
     | 
| 
      
 25 
     | 
    
         
            +
                  @@bot.client.message channel: @channel.slack_id, reply_to: @raw.id, text: msg
         
     | 
| 
      
 26 
     | 
    
         
            +
                end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                def command?
         
     | 
| 
      
 29 
     | 
    
         
            +
                  first = word 0
         
     | 
| 
      
 30 
     | 
    
         
            +
                  return false unless first
         
     | 
| 
      
 31 
     | 
    
         
            +
                  first == @@bot.name || first == "<@#{@@bot.slack_id}>"
         
     | 
| 
      
 32 
     | 
    
         
            +
                end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                def command
         
     | 
| 
      
 35 
     | 
    
         
            +
                  if @channel.type == :im
         
     | 
| 
      
 36 
     | 
    
         
            +
                    command? ? downword(1) : downword(0)
         
     | 
| 
      
 37 
     | 
    
         
            +
                  else
         
     | 
| 
      
 38 
     | 
    
         
            +
                    command? ? downword(1) : nil
         
     | 
| 
      
 39 
     | 
    
         
            +
                  end
         
     | 
| 
      
 40 
     | 
    
         
            +
                end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                def yes?
         
     | 
| 
      
 43 
     | 
    
         
            +
                  [ 'y', 'yes' ].include? @text
         
     | 
| 
      
 44 
     | 
    
         
            +
                end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                def no?
         
     | 
| 
      
 47 
     | 
    
         
            +
                  [ 'n', 'no' ].include? @text
         
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                private
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                def word(n)
         
     | 
| 
      
 53 
     | 
    
         
            +
                  return nil unless @pieces.length > n
         
     | 
| 
      
 54 
     | 
    
         
            +
                  @pieces[n]
         
     | 
| 
      
 55 
     | 
    
         
            +
                end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                def downword(n)
         
     | 
| 
      
 58 
     | 
    
         
            +
                  word(n) && word(n).downcase
         
     | 
| 
      
 59 
     | 
    
         
            +
                end
         
     | 
| 
      
 60 
     | 
    
         
            +
              end
         
     | 
| 
      
 61 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,73 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'fileutils'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            class Slacky::Service
         
     | 
| 
      
 4 
     | 
    
         
            +
              def initialize(config, daemon)
         
     | 
| 
      
 5 
     | 
    
         
            +
                @config = config
         
     | 
| 
      
 6 
     | 
    
         
            +
                @daemon = daemon
         
     | 
| 
      
 7 
     | 
    
         
            +
              end
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
              def run
         
     | 
| 
      
 10 
     | 
    
         
            +
                @daemon.start false
         
     | 
| 
      
 11 
     | 
    
         
            +
              end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
              def start(persist = false)
         
     | 
| 
      
 14 
     | 
    
         
            +
                pid = get_pid
         
     | 
| 
      
 15 
     | 
    
         
            +
                if pid
         
     | 
| 
      
 16 
     | 
    
         
            +
                  puts "#{@config.name} is already running with PID #{pid}"
         
     | 
| 
      
 17 
     | 
    
         
            +
                  return
         
     | 
| 
      
 18 
     | 
    
         
            +
                end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                print "Starting #{@config.name}... "
         
     | 
| 
      
 21 
     | 
    
         
            +
                new_pid = Process.fork { @daemon.start }
         
     | 
| 
      
 22 
     | 
    
         
            +
                Process.detach new_pid
         
     | 
| 
      
 23 
     | 
    
         
            +
                puts "started"
         
     | 
| 
      
 24 
     | 
    
         
            +
              end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
              def stop(persist = false)
         
     | 
| 
      
 27 
     | 
    
         
            +
                pid = get_pid
         
     | 
| 
      
 28 
     | 
    
         
            +
                unless pid
         
     | 
| 
      
 29 
     | 
    
         
            +
                  puts "#{@config.name} is not running"
         
     | 
| 
      
 30 
     | 
    
         
            +
                  return
         
     | 
| 
      
 31 
     | 
    
         
            +
                end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                print "Stopping #{@config.name}..."
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                begin
         
     | 
| 
      
 36 
     | 
    
         
            +
                  Process.kill 'HUP', pid
         
     | 
| 
      
 37 
     | 
    
         
            +
                rescue
         
     | 
| 
      
 38 
     | 
    
         
            +
                  @daemon.cleanup
         
     | 
| 
      
 39 
     | 
    
         
            +
                end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                ticks = 0
         
     | 
| 
      
 42 
     | 
    
         
            +
                while pid = get_pid && ticks < 40
         
     | 
| 
      
 43 
     | 
    
         
            +
                  sleep 0.5
         
     | 
| 
      
 44 
     | 
    
         
            +
                  ticks += 1
         
     | 
| 
      
 45 
     | 
    
         
            +
                  print '.' if ticks % 4 == 0
         
     | 
| 
      
 46 
     | 
    
         
            +
                end
         
     | 
| 
      
 47 
     | 
    
         
            +
                puts " #{pid.nil? ? 'stopped' : 'failed'}"
         
     | 
| 
      
 48 
     | 
    
         
            +
              end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
              def restart
         
     | 
| 
      
 51 
     | 
    
         
            +
                stop
         
     | 
| 
      
 52 
     | 
    
         
            +
                start
         
     | 
| 
      
 53 
     | 
    
         
            +
              end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
              def status
         
     | 
| 
      
 56 
     | 
    
         
            +
                pid = get_pid
         
     | 
| 
      
 57 
     | 
    
         
            +
                if pid
         
     | 
| 
      
 58 
     | 
    
         
            +
                  puts "#{@config.name} is running with PID #{pid}"
         
     | 
| 
      
 59 
     | 
    
         
            +
                  true
         
     | 
| 
      
 60 
     | 
    
         
            +
                else
         
     | 
| 
      
 61 
     | 
    
         
            +
                  puts "#{@config.name} is not running"
         
     | 
| 
      
 62 
     | 
    
         
            +
                  false
         
     | 
| 
      
 63 
     | 
    
         
            +
                end
         
     | 
| 
      
 64 
     | 
    
         
            +
              end
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
              private
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
              def get_pid
         
     | 
| 
      
 69 
     | 
    
         
            +
                return nil unless File.exists? @config.pid_file
         
     | 
| 
      
 70 
     | 
    
         
            +
                IO.read(@config.pid_file).to_i
         
     | 
| 
      
 71 
     | 
    
         
            +
              end
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/slacky/user.rb
    ADDED
    
    | 
         @@ -0,0 +1,91 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'json'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Slacky
         
     | 
| 
      
 4 
     | 
    
         
            +
              class User
         
     | 
| 
      
 5 
     | 
    
         
            +
                attr_accessor :username, :slack_id, :slack_im_id, :first_name, :last_name, :email, :timezone, :presence, :data
         
     | 
| 
      
 6 
     | 
    
         
            +
                attr_reader :tz
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                @@decorator = @@db = nil
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                def self.decorator=(decorator)
         
     | 
| 
      
 11 
     | 
    
         
            +
                  @@decorator = decorator
         
     | 
| 
      
 12 
     | 
    
         
            +
                end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                def self.db=(db)
         
     | 
| 
      
 15 
     | 
    
         
            +
                  @@db = db
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                def self.initialize_table
         
     | 
| 
      
 19 
     | 
    
         
            +
                  @@db.exec <<-SQL
         
     | 
| 
      
 20 
     | 
    
         
            +
            create table if not exists users (
         
     | 
| 
      
 21 
     | 
    
         
            +
              username     varchar(64) not null,
         
     | 
| 
      
 22 
     | 
    
         
            +
              slack_id     varchar(20) not null,
         
     | 
| 
      
 23 
     | 
    
         
            +
              slack_im_id  varchar(20),
         
     | 
| 
      
 24 
     | 
    
         
            +
              first_name   varchar(64),
         
     | 
| 
      
 25 
     | 
    
         
            +
              last_name    varchar(64),
         
     | 
| 
      
 26 
     | 
    
         
            +
              email        varchar(128) not null,
         
     | 
| 
      
 27 
     | 
    
         
            +
              timezone     varchar(256),
         
     | 
| 
      
 28 
     | 
    
         
            +
              presence     varchar(64),
         
     | 
| 
      
 29 
     | 
    
         
            +
              data         jsonb not null
         
     | 
| 
      
 30 
     | 
    
         
            +
            );
         
     | 
| 
      
 31 
     | 
    
         
            +
            SQL
         
     | 
| 
      
 32 
     | 
    
         
            +
                end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                def self.find(user)
         
     | 
| 
      
 35 
     | 
    
         
            +
                  return user.map { |u| User.find u }.compact if user.is_a? Array
         
     | 
| 
      
 36 
     | 
    
         
            +
                  result = @@db.exec_params "select * from users where slack_id = $1", [ user ]
         
     | 
| 
      
 37 
     | 
    
         
            +
                  if result.ntuples == 0
         
     | 
| 
      
 38 
     | 
    
         
            +
                    result = @@db.exec_params "select * from users where username = $1", [ user ]
         
     | 
| 
      
 39 
     | 
    
         
            +
                  end
         
     | 
| 
      
 40 
     | 
    
         
            +
                  return nil if result.ntuples == 0
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                  row = result[0]
         
     | 
| 
      
 43 
     | 
    
         
            +
                  user = self.new username:    row['username'],
         
     | 
| 
      
 44 
     | 
    
         
            +
                                  slack_id:    row['slack_id'],
         
     | 
| 
      
 45 
     | 
    
         
            +
                                  slack_im_id: row['slack_im_id'],
         
     | 
| 
      
 46 
     | 
    
         
            +
                                  first_name:  row['first_name'],
         
     | 
| 
      
 47 
     | 
    
         
            +
                                  last_name:   row['last_name'],
         
     | 
| 
      
 48 
     | 
    
         
            +
                                  email:       row['email'],
         
     | 
| 
      
 49 
     | 
    
         
            +
                                  timezone:    row['timezone'],
         
     | 
| 
      
 50 
     | 
    
         
            +
                                  presence:    row['presence'],
         
     | 
| 
      
 51 
     | 
    
         
            +
                                  data:        JSON.parse(row['data'])
         
     | 
| 
      
 52 
     | 
    
         
            +
                  user.extend @@decorator if @@decorator
         
     | 
| 
      
 53 
     | 
    
         
            +
                  user
         
     | 
| 
      
 54 
     | 
    
         
            +
                end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                def initialize(attrs={})
         
     | 
| 
      
 57 
     | 
    
         
            +
                  @username    = attrs[:username]
         
     | 
| 
      
 58 
     | 
    
         
            +
                  @slack_id    = attrs[:slack_id]
         
     | 
| 
      
 59 
     | 
    
         
            +
                  @slack_im_id = attrs[:slack_im_id]
         
     | 
| 
      
 60 
     | 
    
         
            +
                  @first_name  = attrs[:first_name]
         
     | 
| 
      
 61 
     | 
    
         
            +
                  @last_name   = attrs[:last_name]
         
     | 
| 
      
 62 
     | 
    
         
            +
                  @email       = attrs[:email]
         
     | 
| 
      
 63 
     | 
    
         
            +
                  @timezone    = attrs[:timezone] || "America/Los_Angeles"
         
     | 
| 
      
 64 
     | 
    
         
            +
                  @presence    = attrs[:presence]
         
     | 
| 
      
 65 
     | 
    
         
            +
                  @data        = attrs[:data] || {}
         
     | 
| 
      
 66 
     | 
    
         
            +
                end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                def populate(member)
         
     | 
| 
      
 69 
     | 
    
         
            +
                  @username   = member.name
         
     | 
| 
      
 70 
     | 
    
         
            +
                  @first_name = member.profile.first_name
         
     | 
| 
      
 71 
     | 
    
         
            +
                  @last_name  = member.profile.last_name
         
     | 
| 
      
 72 
     | 
    
         
            +
                  @email      = member.profile.email
         
     | 
| 
      
 73 
     | 
    
         
            +
                  @timezone   = member.tz
         
     | 
| 
      
 74 
     | 
    
         
            +
                  @presence   = member.presence
         
     | 
| 
      
 75 
     | 
    
         
            +
                  @data       = {} unless @data
         
     | 
| 
      
 76 
     | 
    
         
            +
                  self
         
     | 
| 
      
 77 
     | 
    
         
            +
                end
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                def save
         
     | 
| 
      
 80 
     | 
    
         
            +
                  @@db.exec_params "delete from users where slack_id = $1", [ @slack_id ]
         
     | 
| 
      
 81 
     | 
    
         
            +
                  @@db.exec_params "insert into users (username, slack_id, slack_im_id, first_name, last_name, email, timezone, presence, data)
         
     | 
| 
      
 82 
     | 
    
         
            +
                                    values ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
         
     | 
| 
      
 83 
     | 
    
         
            +
                                    [ @username, @slack_id, @slack_im_id, @first_name, @last_name, @email, @timezone, @presence, JSON.dump(@data) ]
         
     | 
| 
      
 84 
     | 
    
         
            +
                  self
         
     | 
| 
      
 85 
     | 
    
         
            +
                end
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
                def reset
         
     | 
| 
      
 88 
     | 
    
         
            +
                  @data = {}
         
     | 
| 
      
 89 
     | 
    
         
            +
                end
         
     | 
| 
      
 90 
     | 
    
         
            +
              end
         
     | 
| 
      
 91 
     | 
    
         
            +
            end
         
     | 
    
        data/slacky.gemspec
    ADDED
    
    | 
         @@ -0,0 +1,31 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # -*- encoding: utf-8 -*-
         
     | 
| 
      
 2 
     | 
    
         
            +
            $LOAD_PATH << File.dirname(__FILE__) + "/lib"
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'slacky/version'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            Gem::Specification.new do |s|
         
     | 
| 
      
 6 
     | 
    
         
            +
              s.name        = "slacky"
         
     | 
| 
      
 7 
     | 
    
         
            +
              s.version     = Slacky::VERSION
         
     | 
| 
      
 8 
     | 
    
         
            +
              s.authors     = ["Michael Wynholds"]
         
     | 
| 
      
 9 
     | 
    
         
            +
              s.email       = ["mike@carbonfive.com"]
         
     | 
| 
      
 10 
     | 
    
         
            +
              s.homepage    = ""
         
     | 
| 
      
 11 
     | 
    
         
            +
              s.summary     = %q{Carbon Five Slack bot gem}
         
     | 
| 
      
 12 
     | 
    
         
            +
              s.description = %q{Carbon Five Slack bot gem}
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
              s.files         = `git ls-files`.split("\n")
         
     | 
| 
      
 15 
     | 
    
         
            +
              s.test_files    = `git ls-files -- {test,spec,features}/*`.split("\n")
         
     | 
| 
      
 16 
     | 
    
         
            +
              s.executables   = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
         
     | 
| 
      
 17 
     | 
    
         
            +
              s.require_paths = ["lib"]
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
              s.add_runtime_dependency 'slack-ruby-client', ">= 0.6"
         
     | 
| 
      
 20 
     | 
    
         
            +
              s.add_runtime_dependency 'pg'
         
     | 
| 
      
 21 
     | 
    
         
            +
              s.add_runtime_dependency 'eventmachine'
         
     | 
| 
      
 22 
     | 
    
         
            +
              s.add_runtime_dependency 'faye-websocket'
         
     | 
| 
      
 23 
     | 
    
         
            +
              s.add_runtime_dependency 'em-cron'
         
     | 
| 
      
 24 
     | 
    
         
            +
              s.add_runtime_dependency 'tzinfo'
         
     | 
| 
      
 25 
     | 
    
         
            +
              s.add_runtime_dependency 'tzinfo-data'
         
     | 
| 
      
 26 
     | 
    
         
            +
              s.add_runtime_dependency 'dotenv'
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
              s.add_development_dependency 'rake'
         
     | 
| 
      
 29 
     | 
    
         
            +
              s.add_development_dependency 'minitest'
         
     | 
| 
      
 30 
     | 
    
         
            +
              s.add_development_dependency 'factory_girl'
         
     | 
| 
      
 31 
     | 
    
         
            +
            end
         
     | 
    
        metadata
    ADDED
    
    | 
         @@ -0,0 +1,245 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            --- !ruby/object:Gem::Specification
         
     | 
| 
      
 2 
     | 
    
         
            +
            name: slacky
         
     | 
| 
      
 3 
     | 
    
         
            +
            version: !ruby/object:Gem::Version
         
     | 
| 
      
 4 
     | 
    
         
            +
              version: '0.1'
         
     | 
| 
      
 5 
     | 
    
         
            +
              prerelease: 
         
     | 
| 
      
 6 
     | 
    
         
            +
            platform: ruby
         
     | 
| 
      
 7 
     | 
    
         
            +
            authors:
         
     | 
| 
      
 8 
     | 
    
         
            +
            - Michael Wynholds
         
     | 
| 
      
 9 
     | 
    
         
            +
            autorequire: 
         
     | 
| 
      
 10 
     | 
    
         
            +
            bindir: bin
         
     | 
| 
      
 11 
     | 
    
         
            +
            cert_chain: []
         
     | 
| 
      
 12 
     | 
    
         
            +
            date: 2016-05-24 00:00:00.000000000 Z
         
     | 
| 
      
 13 
     | 
    
         
            +
            dependencies:
         
     | 
| 
      
 14 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 15 
     | 
    
         
            +
              name: slack-ruby-client
         
     | 
| 
      
 16 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 17 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 18 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 19 
     | 
    
         
            +
                - - ! '>='
         
     | 
| 
      
 20 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 21 
     | 
    
         
            +
                    version: '0.6'
         
     | 
| 
      
 22 
     | 
    
         
            +
              type: :runtime
         
     | 
| 
      
 23 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 24 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 25 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 26 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 27 
     | 
    
         
            +
                - - ! '>='
         
     | 
| 
      
 28 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 29 
     | 
    
         
            +
                    version: '0.6'
         
     | 
| 
      
 30 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 31 
     | 
    
         
            +
              name: pg
         
     | 
| 
      
 32 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 33 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 34 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 35 
     | 
    
         
            +
                - - ! '>='
         
     | 
| 
      
 36 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 37 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 38 
     | 
    
         
            +
              type: :runtime
         
     | 
| 
      
 39 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 40 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 41 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 42 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 43 
     | 
    
         
            +
                - - ! '>='
         
     | 
| 
      
 44 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 45 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 46 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 47 
     | 
    
         
            +
              name: eventmachine
         
     | 
| 
      
 48 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 49 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 50 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 51 
     | 
    
         
            +
                - - ! '>='
         
     | 
| 
      
 52 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 53 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 54 
     | 
    
         
            +
              type: :runtime
         
     | 
| 
      
 55 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 56 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 57 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 58 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 59 
     | 
    
         
            +
                - - ! '>='
         
     | 
| 
      
 60 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 61 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 62 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 63 
     | 
    
         
            +
              name: faye-websocket
         
     | 
| 
      
 64 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 65 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 66 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 67 
     | 
    
         
            +
                - - ! '>='
         
     | 
| 
      
 68 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 69 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 70 
     | 
    
         
            +
              type: :runtime
         
     | 
| 
      
 71 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 72 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 73 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 74 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 75 
     | 
    
         
            +
                - - ! '>='
         
     | 
| 
      
 76 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 77 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 78 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 79 
     | 
    
         
            +
              name: em-cron
         
     | 
| 
      
 80 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 81 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 82 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 83 
     | 
    
         
            +
                - - ! '>='
         
     | 
| 
      
 84 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 85 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 86 
     | 
    
         
            +
              type: :runtime
         
     | 
| 
      
 87 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 88 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 89 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 90 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 91 
     | 
    
         
            +
                - - ! '>='
         
     | 
| 
      
 92 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 93 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 94 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 95 
     | 
    
         
            +
              name: tzinfo
         
     | 
| 
      
 96 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 97 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 98 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 99 
     | 
    
         
            +
                - - ! '>='
         
     | 
| 
      
 100 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 101 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 102 
     | 
    
         
            +
              type: :runtime
         
     | 
| 
      
 103 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 104 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 105 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 106 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 107 
     | 
    
         
            +
                - - ! '>='
         
     | 
| 
      
 108 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 109 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 110 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 111 
     | 
    
         
            +
              name: tzinfo-data
         
     | 
| 
      
 112 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 113 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 114 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 115 
     | 
    
         
            +
                - - ! '>='
         
     | 
| 
      
 116 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 117 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 118 
     | 
    
         
            +
              type: :runtime
         
     | 
| 
      
 119 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 120 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 121 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 122 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 123 
     | 
    
         
            +
                - - ! '>='
         
     | 
| 
      
 124 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 125 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 126 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 127 
     | 
    
         
            +
              name: dotenv
         
     | 
| 
      
 128 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 129 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 130 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 131 
     | 
    
         
            +
                - - ! '>='
         
     | 
| 
      
 132 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 133 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 134 
     | 
    
         
            +
              type: :runtime
         
     | 
| 
      
 135 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 136 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 137 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 138 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 139 
     | 
    
         
            +
                - - ! '>='
         
     | 
| 
      
 140 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 141 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 142 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 143 
     | 
    
         
            +
              name: rake
         
     | 
| 
      
 144 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 145 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 146 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 147 
     | 
    
         
            +
                - - ! '>='
         
     | 
| 
      
 148 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 149 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 150 
     | 
    
         
            +
              type: :development
         
     | 
| 
      
 151 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 152 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 153 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 154 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 155 
     | 
    
         
            +
                - - ! '>='
         
     | 
| 
      
 156 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 157 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 158 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 159 
     | 
    
         
            +
              name: minitest
         
     | 
| 
      
 160 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 161 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 162 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 163 
     | 
    
         
            +
                - - ! '>='
         
     | 
| 
      
 164 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 165 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 166 
     | 
    
         
            +
              type: :development
         
     | 
| 
      
 167 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 168 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 169 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 170 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 171 
     | 
    
         
            +
                - - ! '>='
         
     | 
| 
      
 172 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 173 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 174 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 175 
     | 
    
         
            +
              name: factory_girl
         
     | 
| 
      
 176 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 177 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 178 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 179 
     | 
    
         
            +
                - - ! '>='
         
     | 
| 
      
 180 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 181 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 182 
     | 
    
         
            +
              type: :development
         
     | 
| 
      
 183 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 184 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 185 
     | 
    
         
            +
                none: false
         
     | 
| 
      
 186 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 187 
     | 
    
         
            +
                - - ! '>='
         
     | 
| 
      
 188 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 189 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 190 
     | 
    
         
            +
            description: Carbon Five Slack bot gem
         
     | 
| 
      
 191 
     | 
    
         
            +
            email:
         
     | 
| 
      
 192 
     | 
    
         
            +
            - mike@carbonfive.com
         
     | 
| 
      
 193 
     | 
    
         
            +
            executables: []
         
     | 
| 
      
 194 
     | 
    
         
            +
            extensions: []
         
     | 
| 
      
 195 
     | 
    
         
            +
            extra_rdoc_files: []
         
     | 
| 
      
 196 
     | 
    
         
            +
            files:
         
     | 
| 
      
 197 
     | 
    
         
            +
            - .gitignore
         
     | 
| 
      
 198 
     | 
    
         
            +
            - .ruby-version
         
     | 
| 
      
 199 
     | 
    
         
            +
            - Gemfile
         
     | 
| 
      
 200 
     | 
    
         
            +
            - Gemfile.lock
         
     | 
| 
      
 201 
     | 
    
         
            +
            - README.md
         
     | 
| 
      
 202 
     | 
    
         
            +
            - Rakefile
         
     | 
| 
      
 203 
     | 
    
         
            +
            - lib/slacky.rb
         
     | 
| 
      
 204 
     | 
    
         
            +
            - lib/slacky/bookkeeper.rb
         
     | 
| 
      
 205 
     | 
    
         
            +
            - lib/slacky/bot.rb
         
     | 
| 
      
 206 
     | 
    
         
            +
            - lib/slacky/channel.rb
         
     | 
| 
      
 207 
     | 
    
         
            +
            - lib/slacky/cli.rb
         
     | 
| 
      
 208 
     | 
    
         
            +
            - lib/slacky/config.rb
         
     | 
| 
      
 209 
     | 
    
         
            +
            - lib/slacky/daemon.rb
         
     | 
| 
      
 210 
     | 
    
         
            +
            - lib/slacky/message.rb
         
     | 
| 
      
 211 
     | 
    
         
            +
            - lib/slacky/service.rb
         
     | 
| 
      
 212 
     | 
    
         
            +
            - lib/slacky/user.rb
         
     | 
| 
      
 213 
     | 
    
         
            +
            - lib/slacky/version.rb
         
     | 
| 
      
 214 
     | 
    
         
            +
            - slacky.gemspec
         
     | 
| 
      
 215 
     | 
    
         
            +
            homepage: ''
         
     | 
| 
      
 216 
     | 
    
         
            +
            licenses: []
         
     | 
| 
      
 217 
     | 
    
         
            +
            post_install_message: 
         
     | 
| 
      
 218 
     | 
    
         
            +
            rdoc_options: []
         
     | 
| 
      
 219 
     | 
    
         
            +
            require_paths:
         
     | 
| 
      
 220 
     | 
    
         
            +
            - lib
         
     | 
| 
      
 221 
     | 
    
         
            +
            required_ruby_version: !ruby/object:Gem::Requirement
         
     | 
| 
      
 222 
     | 
    
         
            +
              none: false
         
     | 
| 
      
 223 
     | 
    
         
            +
              requirements:
         
     | 
| 
      
 224 
     | 
    
         
            +
              - - ! '>='
         
     | 
| 
      
 225 
     | 
    
         
            +
                - !ruby/object:Gem::Version
         
     | 
| 
      
 226 
     | 
    
         
            +
                  version: '0'
         
     | 
| 
      
 227 
     | 
    
         
            +
                  segments:
         
     | 
| 
      
 228 
     | 
    
         
            +
                  - 0
         
     | 
| 
      
 229 
     | 
    
         
            +
                  hash: -3373873093616992315
         
     | 
| 
      
 230 
     | 
    
         
            +
            required_rubygems_version: !ruby/object:Gem::Requirement
         
     | 
| 
      
 231 
     | 
    
         
            +
              none: false
         
     | 
| 
      
 232 
     | 
    
         
            +
              requirements:
         
     | 
| 
      
 233 
     | 
    
         
            +
              - - ! '>='
         
     | 
| 
      
 234 
     | 
    
         
            +
                - !ruby/object:Gem::Version
         
     | 
| 
      
 235 
     | 
    
         
            +
                  version: '0'
         
     | 
| 
      
 236 
     | 
    
         
            +
                  segments:
         
     | 
| 
      
 237 
     | 
    
         
            +
                  - 0
         
     | 
| 
      
 238 
     | 
    
         
            +
                  hash: -3373873093616992315
         
     | 
| 
      
 239 
     | 
    
         
            +
            requirements: []
         
     | 
| 
      
 240 
     | 
    
         
            +
            rubyforge_project: 
         
     | 
| 
      
 241 
     | 
    
         
            +
            rubygems_version: 1.8.23
         
     | 
| 
      
 242 
     | 
    
         
            +
            signing_key: 
         
     | 
| 
      
 243 
     | 
    
         
            +
            specification_version: 3
         
     | 
| 
      
 244 
     | 
    
         
            +
            summary: Carbon Five Slack bot gem
         
     | 
| 
      
 245 
     | 
    
         
            +
            test_files: []
         
     |