# 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