# Ruby client for the Lingr[http://www.lingr.com] API. For more details and tutorials, see the
# {Lingr API Reference}[http://wiki.lingr.com/dev/show/API+Reference] pages on the {Lingr Developer Wiki}[http://wiki.lingr.com].
#
# All methods return a hash with two keys:
# * :succeeded - true if the method succeeded, false otherwise
# * :response - a Hash version of the response document received from the server
#
# = api_client.rb
#
# Lingr API client
#
#
# Original written by Lingr.
# Modified by cho45
# * Use json gem instead of gsub/eval.
# * Raise APIError when api fails.
# * Rename class name to Lingr::Client.
$KCODE = 'u' # used by json
require "rubygems"
require "net/http"
require "json"
require "uri"
require "timeout"
module Lingr
class Client
class ClientError < StandardError; end
class APIError < ClientError
def initialize(error)
@error = error || {
"message" => "socket error",
"code" => 0,
}
super(@error["message"])
end
def code
@error["code"]
end
end
attr_accessor :api_key
# 0 = quiet, 1 = some debug info, 2 = more debug info
attr_accessor :verbosity
attr_accessor :session
attr_accessor :timeout
def initialize(api_key, verbosity=0, hostname='www.lingr.com')
@api_key = api_key
@host = hostname
@verbosity = verbosity
@timeout = 60
end
# Create a new API session
#
def create_session(client_type='automaton')
if @session
@error_info = nil
raise ClientError, "already in a session"
end
ret = do_api :post, 'session/create', { :api_key => @api_key, :client_type => client_type }, false
@session = ret["session"]
ret
end
# Verify a session id. If no session id is passed, verifies the current session id for this ApiClient
#
def verify_session(session_id=nil)
do_api :get, 'session/verify', { :session => session_id || @session }, false
end
# Destroy the current API session
#
def destroy_session
ret = do_api :post, 'session/destroy', { :session => @session }
@session = nil
ret
end
# Get a list of the currently hot rooms
#
def get_hot_rooms(count=nil)
do_api :get, 'explore/get_hot_rooms', { :api_key => @api_key }.merge(count ? { :count => count} : {}), false
end
# Get a list of the newest rooms
#
def get_new_rooms(count=nil)
do_api :get, 'explore/get_new_rooms', { :api_key => @api_key }.merge(count ? { :count => count} : {}), false
end
# Get a list of the currently hot tags
#
def get_hot_tags(count=nil)
do_api :get, 'explore/get_hot_tags', { :api_key => @api_key }.merge(count ? { :count => count} : {}), false
end
# Get a list of all tags
#
def get_all_tags(count=nil)
do_api :get, 'explore/get_all_tags', { :api_key => @api_key }.merge(count ? { :count => count} : {}), false
end
# Search room name, description, and tags for keywords. Keywords can be a String or an Array.
#
def search(keywords)
do_api :get, 'explore/search', { :api_key => @api_key, :q => keywords.is_a?(Array) ? keywords.join(',') : keywords }, false
end
# Search room tags. Tagnames can be a String or an Array.
#
def search_tags(tagnames)
do_api :get, 'explore/search_tags', { :api_key => @api_key, :q => tagnames.is_a?(Array) ? tagnames.join(',') : tagnames }, false
end
# Search archives. If room_id is non-nil, the search is limited to the archives of that room.
#
def search_archives(query, room_id=nil)
params = { :api_key => @api_key, :q => query }
params.merge!({ :id => room_id }) if room_id
do_api :get, 'explore/search_archives', params, false
end
# Authenticate a user within the current API session
#
def login(email, password)
do_api :post, 'auth/login', { :session => @session, :email => email, :password => password }
end
# Log out the currently-authenticated user in the session, if any
#
def logout
do_api :post, 'auth/logout', { :session => @session }
end
# Get information about the currently-authenticated user
#
def get_user_info
do_api :get, 'user/get_info', { :session => @session }
end
# Start observing the currently-authenticated user
#
def start_observing_user
do_api :post, 'user/start_observing', { :session => @session }
end
# Observe the currently-authenticated user, watching for profile changes
#
def observe_user(ticket, counter)
do_api :get, 'user/observe', { :session => @session, :ticket => ticket, :counter => counter }
end
# Stop observing the currently-authenticated user
#
def stop_observing_user(ticket)
do_api :post, 'user/stop_observing', { :session => @session, :ticket =>ticket }
end
# Get information about a chatroom, including room description, current occupants, recent messages, etc.
#
def get_room_info(room_id, counter=nil, password=nil)
params = { :api_key => @api_key, :id => room_id }
params.merge!({ :counter => counter }) if counter
params.merge!({ :password => password }) if password
do_api :get, 'room/get_info', params, false
end
# Create a chatroom
#
# options is a Hash containing any of the parameters allowed for room.create. If the :image key is present
# in options, its value must be a hash with the keys :filename, :mime_type, and :io
#
def create_room(options)
do_api :post, 'room/create', options.merge({ :session => @session })
end
# Change the settings for a chatroom
#
# options is a Hash containing any of the parameters allowed for room.create. If the :image key is present
# in options, its value must be a hash with the keys :filename, :mime_type, and :io. To change the id for
# a room, use the key :new_id
#
def change_settings(room_id, options)
do_api :post, 'room/change_settings', options.merge({ :session => @session })
end
# Delete a chatroom
#
def delete_room(room_id)
do_api :post, 'room/delete', { :id => room_id, :session => @session }
end
# Enter a chatroom
#
def enter_room(room_id, nickname=nil, password=nil, idempotent=false)
params = { :session => @session, :id => room_id }
params.merge!({ :nickname => nickname }) if nickname
params.merge!({ :password => password }) if password
params.merge!({ :idempotent => 'true' }) if idempotent
do_api :post, 'room/enter', params
end
# Poll for messages in a chatroom
#
def get_messages(ticket, counter, user_messages_only=false)
do_api :get, 'room/get_messages', { :session => @session, :ticket => ticket, :counter => counter, :user_messages_only => user_messages_only }
end
# Observe a chatroom, waiting for events to occur in the room
#
def observe_room(ticket, counter)
do_api :get, 'room/observe', { :session => @session, :ticket => ticket, :counter => counter }
end
# Set your nickname in a chatroom
#
def set_nickname(ticket, nickname)
do_api :post, 'room/set_nickname', { :session => @session, :ticket => ticket, :nickname => nickname }
end
# Say something in a chatroom. If target_occupant_id is not nil, a private message
# is sent to the indicated occupant.
#
def say(ticket, msg, target_occupant_id = nil)
params = { :session => @session, :ticket => ticket, :message => msg }
params.merge!({ :occupant_id => target_occupant_id}) if target_occupant_id
do_api :post, 'room/say', params
end
# Exit a chatroom
#
def exit_room(ticket)
do_api :post, 'room/exit', { :session => @session, :ticket => ticket }
end
private
def do_api(method, path, parameters, require_session=true)
if require_session and !@session
raise ClientError, "not in a session"
end
response = Timeout.timeout(@timeout) {
JSON.parse(self.send(method, url_for(path), parameters.merge({ :format => 'json' })))
}
unless success?(response)
raise APIError, response["error"]
end
response
end
def url_for(method)
"http://#{@host}/#{@@PATH_BASE}#{method}"
end
def get(url, params)
uri = URI.parse(url)
path = uri.path
q = params.inject("?") {|s, p| s << "#{p[0].to_s}=#{URI.encode(p[1].to_s, /./)}&"}.chop
path << q if q.length > 0
Net::HTTP.start(uri.host, uri.port) do |http|
http.read_timeout = @timeout
req = Net::HTTP::Get.new(path)
req.basic_auth(uri.user, uri.password) if uri.user
parse_result http.request(req)
end
end
def post(url, params)
if !params.find {|p| p[1].is_a?(Hash)}
params = params.inject({}){|hash,(k,v)| hash[k.to_s] = v; hash}
parse_result Net::HTTP.post_form(URI.parse(url), params)
else
boundary = 'lingr-api-client' + (0x1000000 + rand(0x1000000).to_s(16))
query = params.collect { |p|
ret = ["--#{boundary}"]
if p[1].is_a?(Hash)
ret << "Content-Disposition: form-data; name=\"#{URI.encode(p[0].to_s)}\"; filename=\"#{p[1][:filename]}\""
ret << "Content-Transfer-Encoding: binary"
ret << "Content-Type: #{p[1][:mime_type]}"
ret << ""
ret << p[1][:io].read
else
ret << "Content-Disposition: form-data; name=\"#{URI.encode(p[0].to_s)}\""
ret << ""
ret << p[1]
end
ret.join("\r\n")
}.join('') + "--#{boundary}--\r\n"
uri = URI.parse(url)
Net::HTTP.start(uri.host, uri.port) do |http|
http.read_timeout = @timeout
parse_result http.post2(uri.path, query, "Content-Type" => "multipart/form-data; boundary=#{boundary}")
end
end
end
def parse_result(result)
return nil if !result || result.code != '200' || (!result['Content-Type'] || result['Content-Type'].index('text/javascript') != 0)
# puts
# puts
# puts result.body
# puts
# puts
result.body
end
def success?(response)
return false if !response
response["status"] and response["status"] == 'ok'
end
@@PATH_BASE = 'api/'
end
end