class Ferris::RTM

Constants

HTTPS_PORT
SOCKET_READ_SIZE

Public Class Methods

new(options={}) click to toggle source
# File lib/ferris/rtm.rb, line 31
def initialize options={}
  @id        = 0
  @jobs      = []
  @stopped   = false
  @callbacks = Hash.new { |h,k| h[k] = [] }
  @q         = Queue.new
  @qlock     = Mutex.new
  @token     = options.fetch :token
  @api_url   = options.fetch :api_url, 'https://slack.com/api'
  @logger    = options.fetch :logger, Slog.new
  @wss_uri   = construct_wss_uri @token, @api_url
  log.trace event: 'RTM client initialized', wss_uri: @wss_uri.to_s
end

Public Instance Methods

go() click to toggle source
# File lib/ferris/rtm.rb, line 66
def go
  log.trace event: 'connecting'
  socket, driver = connect
  log.debug event: 'connected'
  @jobs << go_read_input(socket, driver)
  @jobs << go_write_output(socket, driver)
  @jobs
end
join() click to toggle source
# File lib/ferris/rtm.rb, line 78
def join ; @jobs.map(&:join) end
kill() click to toggle source
# File lib/ferris/rtm.rb, line 76
def kill ; @jobs.map(&:kill) end
on(event, &block) click to toggle source
# File lib/ferris/rtm.rb, line 46
def on event, &block
  unless [ :open, :close, :error, :message ].include? event
    log.fatal event: 'invalid event', on_event: event
    raise ArgumentError
  end

  log.debug event: 'adding callback', on_event: event
  @callbacks[event] << block
end
send(type, message={}) click to toggle source
# File lib/ferris/rtm.rb, line 57
def send type, message={}
  log.debug event: 'sending message', message: message
  message[:id] ||= next_id
  message[:type] = type
  enqueue message
  nil
end

Private Instance Methods

connect() click to toggle source
# File lib/ferris/rtm.rb, line 117
def connect
  tcp_socket = TCPSocket.new @wss_uri.host, HTTPS_PORT
  ssl_socket = OpenSSL::SSL::SSLSocket.new tcp_socket
  ssl_socket.connect

  writer = WebSocketWriter.new @wss_uri.to_s, ssl_socket
  driver = WebSocket::Driver.client writer

  driver.on :open do |_|
    log.trace event: 'driver open'
    @callbacks[:open].map(&:call)
  end

  driver.on :close do |_|
    log.trace event: 'driver close'
    @callbacks[:close].map(&:call)
  end

  driver.on :error do |_|
    log.trace event: 'driver error'
    @callbacks[:error].map(&:call)
  end

  driver.on :message do |event|
    log.trace event: 'driver message', message: event
    data = JSON.parse event.data
    @callbacks[:message].map { |c| c.call data }
  end

  driver.start
  [ ssl_socket, driver ]
end
construct_wss_uri(token, api_url) click to toggle source
# File lib/ferris/rtm.rb, line 87
def construct_wss_uri token, api_url
  uri = URI File.join(api_url, 'rtm.start')
  req = Net::HTTP.post_form uri, token: token
  bod = JSON.parse req.body, symbolize_names: true
  URI bod[:url]

rescue
  x = { event: 'could not construct WSS URI' }
  x[:error] = bod[:error] if bod && bod[:error]
  log.fatal x
  raise RuntimeError
end
enqueue(message) click to toggle source
# File lib/ferris/rtm.rb, line 106
def enqueue message
  @qlock.synchronize { @q << message }
end
full_dequeue(q=[]) click to toggle source
# File lib/ferris/rtm.rb, line 111
def full_dequeue q=[]
  nq = @qlock.synchronize { q << @q.shift until @q.empty? ; q }
  nq
end
go_read_input(socket, driver) click to toggle source
# File lib/ferris/rtm.rb, line 151
def go_read_input socket, driver
  Thread.new do
    loop do
      log.trace event: 'reading socket'
      data = socket.readpartial SOCKET_READ_SIZE
      log.debug event: 'read socket', data_size: data.size
      next if data.nil? || data.empty?
      driver.parse data
    end
  end
end
go_write_output(socket, driver, delay=0.1) click to toggle source
# File lib/ferris/rtm.rb, line 164
def go_write_output socket, driver, delay=0.1
  Thread.new do
    loop do
      q = full_dequeue

      q.each do |m|
        sent = driver.text JSON.generate(m)
        unless sent
          log.fatal event: 'could not send', message: m
          raise RuntimeError
        end
      end

      sleep delay if q.empty?
    end
  end
end
log() click to toggle source
# File lib/ferris/rtm.rb, line 84
def log ; @logger end
next_id() click to toggle source
# File lib/ferris/rtm.rb, line 101
def next_id
  @qlock.synchronize { @id += 1 }
end