lib/slack-rtmapi/client.rb
require 'json'
require 'socket'
require 'websocket/driver'
module SlackRTM
class Client
attr_accessor :stop
def initialize(conf = {})
if conf[:websocket_url].nil?
raise ArgumentError.new 'conf[:websocket_url] should be provided'
else
@url = init_url conf[:websocket_url]
end
@socket = conf[:socket]
@driver = conf[:websocket_driver]
@msg_queue = conf[:msg_queue] || []
@has_been_init = false
@stop = false
end
VALID = [:open, :message, :error]
def on(type, &block)
unless VALID.include? type
raise ArgumentError.new "Client#on accept one of #{VALID.inspect}"
end
@callbacks ||= {}
@callbacks[type] = block
end
def send(data)
data[:id] ||= SecureRandom.random_number 9999999
@msg_queue << data.to_json
end
# This init has been delayed because the SSL handshake is a blocking and
# expensive call
def init
return if @has_been_init
@socket = init_socket(@socket)
@socket.connect # costly and blocking !
internalWrapper = (Struct.new :url, :socket do
def write(*args)
self.socket.write(*args)
end
end).new @url.to_s, @socket
# this, also, is costly and blocking
@driver = WebSocket::Driver.client internalWrapper
@driver.on :open do
@connected = true
unless @callbacks[:open].nil?
@callbacks[:open].call
end
end
@driver.on :error do |event|
@connected = false
unless @callbacks[:error].nil?
@callbacks[:error].call
end
end
@driver.on :message do |event|
data = JSON.parse event.data
unless @callbacks[:message].nil?
@callbacks[:message].call data
end
end
@driver.start
@has_been_init = true
end
def connected?
@connected || false
end
# All the polling work is done here
def inner_loop
return if @stop
data = @socket.readpartial 4096
return if data.nil? or data.empty?
@driver.parse data
@msg_queue.each {|msg| @driver.text msg}
@msg_queue.clear
end
# A dumb simple main loop.
def main_loop
init
loop do
inner_loop
end
end
private
def init_url(url)
url = if url.kind_of? URI then url else URI(url) end
if url.scheme != 'wss'
raise ArgumentError.new "config[:websocket_url] should be a valid websocket secure url !"
end
url
end
def init_socket(socket = nil)
if socket.kind_of? OpenSSL::SSL::SSLSocket
socket
elsif socket.kind_of? TCPSocket
OpenSSL::SSL::SSLSocket.new socket
else
OpenSSL::SSL::SSLSocket.new TCPSocket.new @url.host, 443
end
end
end
end