lib/xmpp4r/robot.rb in xmpp4r-robot-0.1 vs lib/xmpp4r/robot.rb in xmpp4r-robot-0.2.0

- old
+ new

@@ -1,6 +1,8 @@ +require 'set' + require 'xmpp4r' require 'xmpp4r/roster' class Jabber::Robot attr_accessor :username, :password, :client, :errback, @@ -8,97 +10,118 @@ def initialize username, password, opts={}, &errback @username = username @password = password @errback = errback + @roster = {:available => Set.new, + :away => Set.new, + :unavailable => Set.new} @retry_time = Float(opts[:retry_time] || 0) @auto_accept_subscription = opts[:auto_accept_subscription] end - # So that you could reset the client by calling: - # robot.client = nil - # robot.client - # This would remove all previously set callbacks. + def inspect + "#<#{self.class.name} username=#{username.inspect}>" + end + def client @client ||= Jabber::Client.new(Jabber::JID::new(username)) end + def helper + @helper ||= Jabber::Roster::Helper.new(client, false) + end + def start + if @client # restart + stop + @client = nil + end + initialize_callbacks connect login - initialize_callbacks available + self end def stop client.close + self end def connect client.connect + self end def login client.auth(password) + self end + ##### getters ##### + + def roster sync=false + sync_roster if sync || @roster[:unknown].nil? + @roster + end + ##### actions ##### def available client.send(Jabber::Presence.new.set_type(:available)) end # e.g. robot.message('someone@example.com', 'Hi!') - def message to, body - client.send(Jabber::Message::new(to, body).set_type(:chat)) + def message to_jid, body + client.send(Jabber::Message::new(to_jid, body).set_type(:chat)) end # e.g. robot.subscribe('someone@example.com') - def subscribe to - roster.accept_subscription(to) + def subscribe to_jid + client.send(Jabber::Presence.new.set_type(:subscribe).set_to(to_jid)) end ##### callbacks ##### - # e.g. robot.notify_presence{ |from, reason| puts "#{from} is #{reason}" } + # e.g. robot.notify_presence{ |from, status| puts "#{from} is #{status}" } + # The status could be one of :available, :away, :unavailable def notify_presence client.add_presence_callback do |presence| - # :available, :away, :unavailable - show = presence.type || - case presence.show # http://xmpp.org/rfcs/rfc3921.html - when nil, :chat - :available - when :away, :dnd, :xa - :away - else - raise "What's this show? #{presence.show}" - end + status = presence.type || + case presence.show # http://xmpp.org/rfcs/rfc3921.html + when nil, :chat + :available + when :away, :dnd, :xa + :away + else + raise "What's this show? #{presence.show}" + end protect_yield do - yield(jid_to_username(presence.from), show) + yield(jid_to_username(presence.from), status) + false end end end # e.g. robot.notify_message{ |from, body| puts "#{from}: #{body}" } def notify_message client.add_message_callback do |message| protect_yield do - yield(jid_to_username(message.from), message.body.strip) if - message.body + if message.body + yield(jid_to_username(message.from), message.body.strip) + end + false end end end ##### private ##### private - def roster - @roster ||= Jabber::Roster::Helper.new(client) - end - def initialize_callbacks client.on_exception do |exp| errback.call(exp) if errback next unless retry_time == 0.0 @@ -107,20 +130,50 @@ " We'll sleep for #{retry_time} seconds and retry." sleep(retry_time) start end - roster.add_subscription_request_callback do |_, presence| - subscribe(presence.from) if auto_accept_subscription + client.add_presence_callback do |presence| + if auto_accept_subscription && presence.type == :subscribe + subscribe(presence.from) + end + false end + + notify_presence do |jid, status| + @roster[:unknown].delete(jid) if @roster[:unknown] + + [:available, :away, :unavailable].each do |type| + if type == status + @roster[type].add(jid) + else + @roster[type].delete(jid) + end + end + end end def jid_to_username jid "#{jid.node}@#{jid.domain}" end def protect_yield yield rescue => e errback.call(e) if errback + end + + def sync_roster + clear_roster_semaphore + helper.get_roster + helper.wait_for_roster + @roster[:unknown] = + Set.new(helper.items.keys.map(&method(:jid_to_username))) - + @roster[:available] - @roster[:away] - @roster[:unavailable] + end + + # a hack to let us always get the latest roster + def clear_roster_semaphore + helper.instance_variable_get(:@roster_wait). + instance_variable_set(:@tickets, 0) end end