Class | Jabber::Roster::Helper |
In: |
lib/xmpp4r/roster/helper/roster.rb
|
Parent: | Object |
The Roster helper intercepts <iq/> stanzas with Jabber::IqQueryRoster and <presence/> stanzas, but provides cbs which allow the programmer to keep track of updates.
A thread for any received stanza is spawned, so the user can invoke accept_subscription et al in the callback blocks, without stopping the current (= parser) thread when waiting for a reply.
items | [R] |
All items in your roster
|
Initialize a new Roster helper
Registers its cbs (prio = 120, ref = self)
Request a roster (Remember to send initial presence afterwards!)
The initialization will not wait for the roster being received, use wait_for_roster.
Attention: If you send presence and receive presences before the roster has arrived, the Roster helper will let them pass through and does not keep them!
# File lib/xmpp4r/roster/helper/roster.rb, line 39 39: def initialize(stream) 40: @stream = stream 41: @items = {} 42: @items_lock = Mutex.new 43: @roster_wait = Semaphore.new 44: @query_cbs = CallbackList.new 45: @update_cbs = CallbackList.new 46: @presence_cbs = CallbackList.new 47: @subscription_cbs = CallbackList.new 48: @subscription_request_cbs = CallbackList.new 49: 50: # Register cbs 51: stream.add_iq_callback(120, self) { |iq| 52: if iq.query.kind_of?(IqQueryRoster) 53: Thread.new do 54: Thread.current.abort_on_exception = true 55: handle_iq_query_roster(iq) 56: end 57: 58: true 59: else 60: false 61: end 62: } 63: stream.add_presence_callback(120, self) { |pres| 64: Thread.new do 65: Thread.current.abort_on_exception = true 66: handle_presence(pres) 67: end 68: } 69: 70: # Request the roster 71: rosterget = Iq.new_rosterget 72: stream.send(rosterget) 73: end
Get an item by jid
If not available tries to look for it with the resource stripped
# File lib/xmpp4r/roster/helper/roster.rb, line 244 244: def [](jid) 245: jid = JID.new(jid) unless jid.kind_of? JID 246: 247: @items_lock.synchronize { 248: if @items.has_key?(jid) 249: @items[jid] 250: elsif @items.has_key?(jid.strip) 251: @items[jid.strip] 252: else 253: nil 254: end 255: } 256: end
Accept a subscription request
jid: | [JID] of contact |
iname: | [String] Optional roster item name |
# File lib/xmpp4r/roster/helper/roster.rb, line 347 347: def accept_subscription(jid, iname=nil) 348: pres = Presence.new.set_type(:subscribed).set_to(jid.strip) 349: @stream.send(pres) 350: 351: unless self[jid.strip] 352: request = Iq.new_rosterset 353: request.query.add(Jabber::Roster::RosterItem.new(jid.strip, iname)) 354: @stream.send_with_id(request) 355: end 356: end
Add a user to your roster
Threading is encouraged as the function waits for a result. ServerError is thrown upon error.
See Jabber::Roster::Helper::RosterItem#subscribe for details about subscribing. (This method isn‘t used here but the same functionality applies.)
If the item is already in the local roster it will simply send itself
jid: | [JID] to add |
iname: | [String] Optional item name |
subscribe: | [Boolean] Whether to subscribe to this jid |
# File lib/xmpp4r/roster/helper/roster.rb, line 323 323: def add(jid, iname=nil, subscribe=false) 324: if self[jid] 325: self[jid].send 326: else 327: request = Iq.new_rosterset 328: request.query.add(Jabber::Roster::RosterItem.new(jid, iname)) 329: @stream.send_with_id(request) 330: # Adding to list is handled by handle_iq_query_roster 331: end 332: 333: if subscribe 334: # Actually the item *should* already be known now, 335: # but we do it manually to exclude conditions. 336: pres = Presence.new.set_type(:subscribe).set_to(jid.strip) 337: @stream.send(pres) 338: end 339: end
Add a callback for Jabber::Presence updates
This will be called for <presence/> stanzas for known RosterItems. Unknown JIDs may still pass and can be caught via Jabber::Stream#add_presence_callback.
The block receives three objects:
# File lib/xmpp4r/roster/helper/roster.rb, line 116 116: def add_presence_callback(prio = 0, ref = nil, &block) 117: @presence_cbs.add(prio, ref, block) 118: end
Add a callback to be called when a query has been processed
Because update callbacks are called for each roster item, this may be appropriate to notify that anything has updated.
Arguments for callback block: The received <iq/> stanza
# File lib/xmpp4r/roster/helper/roster.rb, line 89 89: def add_query_callback(prio = 0, ref = nil, &block) 90: @query_cbs.add(prio, ref, block) 91: end
Add a callback for subscription updates, which will be called upon receiving a <presence/> stanza with type:
The block receives two objects:
# File lib/xmpp4r/roster/helper/roster.rb, line 131 131: def add_subscription_callback(prio = 0, ref = nil, &block) 132: @subscription_cbs.add(prio, ref, block) 133: end
Add a callback for subscription requests, which will be called upon receiving a <presence type=‘subscribe’/> stanza
The block receives two objects:
Response to this event can be taken with accept_subscription and decline_subscription.
Example usage:
my_roster.add_subscription_request_callback do |item,presence| if accept_subscription_requests my_roster.accept_subscription(presence.from) else my_roster.decline_subscription(presence.from) end end
# File lib/xmpp4r/roster/helper/roster.rb, line 154 154: def add_subscription_request_callback(prio = 0, ref = nil, &block) 155: @subscription_request_cbs.add(prio, ref, block) 156: end
Add a callback for Jabber::Roster::Helper::RosterItem updates
Note that this will be called much after initialization for the answer of the initial roster request
The block receives two objects:
# File lib/xmpp4r/roster/helper/roster.rb, line 102 102: def add_update_callback(prio = 0, ref = nil, &block) 103: @update_cbs.add(prio, ref, block) 104: end
Decline a subscription request
# File lib/xmpp4r/roster/helper/roster.rb, line 361 361: def decline_subscription(jid) 362: pres = Presence.new.set_type(:unsubscribed).set_to(jid.strip) 363: @stream.send(pres) 364: end
Returns the list of RosterItems which, stripped, are equal to the one you are looking for.
# File lib/xmpp4r/roster/helper/roster.rb, line 261 261: def find(jid) 262: jid = JID.new(jid) unless jid.kind_of? JID 263: 264: j = jid.strip 265: l = {} 266: @items_lock.synchronize { 267: @items.each_pair do |k, v| 268: l[k] = v if k.strip == j 269: end 270: } 271: l 272: end
Get items in a group
When group is nil, return ungrouped items
group: | [String] Group name |
result: | Array of [RosterItem] |
# File lib/xmpp4r/roster/helper/roster.rb, line 297 297: def find_by_group(group) 298: res = [] 299: @items_lock.synchronize { 300: @items.each_pair do |jid,item| 301: res.push(item) if item.groups.include?(group) 302: res.push(item) if item.groups == [] and group.nil? 303: end 304: } 305: res 306: end
Groups in this Roster, sorted by name
Contains nil if there are ungrouped items
result: | [Array] containing group names (String) |
# File lib/xmpp4r/roster/helper/roster.rb, line 280 280: def groups 281: res = [] 282: @items_lock.synchronize { 283: @items.each_pair do |jid,item| 284: res += item.groups 285: res += [nil] if item.groups == [] 286: end 287: } 288: res.uniq.sort { |a,b| a.to_s <=> b.to_s } 289: end
Wait for first roster query result to arrive
# File lib/xmpp4r/roster/helper/roster.rb, line 77 77: def wait_for_roster 78: @roster_wait.wait 79: @roster_wait.run 80: end
Handle received <iq/> stanzas, used internally
# File lib/xmpp4r/roster/helper/roster.rb, line 163 163: def handle_iq_query_roster(iq) 164: # If the <iq/> contains <error/> we just ignore that 165: # and assume an empty roster 166: iq.query.each_element('item') do |item| 167: olditem, newitem = nil, nil 168: 169: @items_lock.synchronize { 170: olditem = @items[item.jid] 171: 172: # Handle deletion of item 173: if item.subscription == :remove 174: @items.delete(item.jid) 175: else 176: newitem = @items[item.jid] = RosterItem.new(@stream).import(item) 177: end 178: } 179: @update_cbs.process(olditem, newitem) 180: end 181: 182: @roster_wait.run 183: @query_cbs.process(iq) 184: end
Handle received <presence/> stanzas, used internally
# File lib/xmpp4r/roster/helper/roster.rb, line 189 189: def handle_presence(pres) 190: item = self[pres.from] 191: 192: if [:subscribed, :unsubscribe, :unsubscribed].include?(pres.type) 193: @subscription_cbs.process(item, pres) 194: true 195: 196: elsif pres.type == :subscribe 197: @subscription_request_cbs.process(item, pres) 198: true 199: 200: else 201: unless item.nil? 202: update_presence(item, pres) 203: true # Callback consumed stanza 204: else 205: false # Callback did not consume stanza 206: end 207: end 208: end
Update the presence of an item, used internally
Callbacks are called here
# File lib/xmpp4r/roster/helper/roster.rb, line 215 215: def update_presence(item, pres) 216: 217: # This requires special handling, to announce all resources offline 218: if pres.from.resource.nil? and pres.type == :error 219: oldpresences = [] 220: item.each_presence do |oldpres| 221: oldpresences << oldpres 222: end 223: 224: item.add_presence(pres) 225: oldpresences.each { |oldpres| 226: @presence_cbs.process(item, oldpres, pres) 227: } 228: else 229: oldpres = item.presence(pres.from).nil? ? 230: nil : 231: Presence.new.import(item.presence(pres.from)) 232: 233: item.add_presence(pres) 234: @presence_cbs.process(item, oldpres, pres) 235: end 236: end