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 add_query_callback to get notifyed when Roster::Helper#items has been filled.
# File lib/xmpp4r/roster/helper/roster.rb, line 36 36: def initialize(stream) 37: @stream = stream 38: @items = {} 39: @items_lock = Mutex.new 40: @query_cbs = CallbackList.new 41: @update_cbs = CallbackList.new 42: @presence_cbs = CallbackList.new 43: @subscription_cbs = CallbackList.new 44: @subscription_request_cbs = CallbackList.new 45: 46: # Register cbs 47: stream.add_iq_callback(120, self) { |iq| 48: Thread.new do 49: handle_iq(iq) 50: end 51: } 52: stream.add_presence_callback(120, self) { |pres| 53: Thread.new do 54: handle_presence(pres) 55: end 56: } 57: 58: # Request the roster 59: rosterget = Iq.new_rosterget 60: stream.send(rosterget) 61: 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 239 239: def [](jid) 240: jid = JID.new(jid) unless jid.kind_of? JID 241: 242: @items_lock.synchronize { 243: if @items.has_key?(jid) 244: @items[jid] 245: elsif @items.has_key?(jid.strip) 246: @items[jid.strip] 247: else 248: nil 249: end 250: } 251: end
Accept a subscription request
jid: | [JID] of contact |
iname: | [String] Optional roster item name |
# File lib/xmpp4r/roster/helper/roster.rb, line 342 342: def accept_subscription(jid, iname=nil) 343: pres = Presence.new.set_type(:subscribed).set_to(jid.strip) 344: @stream.send(pres) 345: 346: unless self[jid.strip] 347: request = Iq.new_rosterset 348: request.query.add(Jabber::Roster::RosterItem.new(jid.strip, iname)) 349: @stream.send_with_id(request) { true } 350: end 351: end
Add a user to your roster
Threading is encouraged as the function waits for a result. ErrorException 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 318 318: def add(jid, iname=nil, subscribe=false) 319: if self[jid] 320: self[jid].send 321: else 322: request = Iq.new_rosterset 323: request.query.add(Jabber::Roster::RosterItem.new(jid, iname)) 324: @stream.send_with_id(request) { true } 325: # Adding to list is handled by handle_iq 326: end 327: 328: if subscribe 329: # Actually the item *should* already be known now, 330: # but we do it manually to exclude conditions. 331: pres = Presence.new.set_type(:subscribe).set_to(jid.strip) 332: @stream.send(pres) 333: end 334: 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 97 97: def add_presence_callback(prio = 0, ref = nil, &block) 98: @presence_cbs.add(prio, ref, block) 99: 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 70 70: def add_query_callback(prio = 0, ref = nil, &block) 71: @query_cbs.add(prio, ref, block) 72: 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 112 112: def add_subscription_callback(prio = 0, ref = nil, &block) 113: @subscription_cbs.add(prio, ref, block) 114: 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 135 135: def add_subscription_request_callback(prio = 0, ref = nil, &block) 136: @subscription_request_cbs.add(prio, ref, block) 137: 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 83 83: def add_update_callback(prio = 0, ref = nil, &block) 84: @update_cbs.add(prio, ref, block) 85: end
Decline a subscription request
# File lib/xmpp4r/roster/helper/roster.rb, line 356 356: def decline_subscription(jid) 357: pres = Presence.new.set_type(:unsubscribed).set_to(jid.strip) 358: @stream.send(pres) 359: 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 256 256: def find(jid) 257: jid = JID.new(jid) unless jid.kind_of? JID 258: 259: j = jid.strip 260: l = {} 261: @items_lock.synchronize { 262: @items.each_pair do |k, v| 263: l[k] = v if k.strip == j 264: end 265: } 266: l 267: 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 292 292: def find_by_group(group) 293: res = [] 294: @items_lock.synchronize { 295: @items.each_pair do |jid,item| 296: res.push(item) if item.groups.include?(group) 297: res.push(item) if item.groups == [] and group.nil? 298: end 299: } 300: res 301: 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 275 275: def groups 276: res = [] 277: @items_lock.synchronize { 278: @items.each_pair do |jid,item| 279: res += item.groups 280: res += [nil] if item.groups == [] 281: end 282: } 283: res.uniq.sort { |a,b| a.to_s <=> b.to_s } 284: end
Handle received <iq/> stanzas, used internally
# File lib/xmpp4r/roster/helper/roster.rb, line 144 144: def handle_iq(iq) 145: if iq.query.kind_of?(IqQueryRoster) 146: # If the <iq/> contains <error/> we just ignore that 147: # and assume an empty roster 148: iq.query.each_element('item') do |item| 149: # Handle deletion of item 150: if item.subscription == :remove 151: @items_lock.synchronize { 152: @items.delete(item.jid) 153: } 154: 155: else 156: olditem = nil 157: @items_lock.synchronize { 158: if @items.has_key?(item.jid) 159: olditem = RosterItem.new(@stream).import(@items[item.jid]) 160: 161: # Clear first, because import doesn't 162: @items[item.jid].iname = nil 163: @items[item.jid].subscription = nil 164: @items[item.jid].ask = nil 165: 166: @items[item.jid].import(item) 167: else 168: @items[item.jid] = RosterItem.new(@stream).import(item) 169: end 170: } 171: @update_cbs.process(olditem, @items[item.jid]) 172: end 173: end 174: 175: @query_cbs.process(iq) 176: else 177: false 178: end 179: end
Handle received <presence/> stanzas, used internally
# File lib/xmpp4r/roster/helper/roster.rb, line 184 184: def handle_presence(pres) 185: item = self[pres.from] 186: 187: if [:subscribed, :unsubscribe, :unsubscribed].include?(pres.type) 188: @subscription_cbs.process(item, pres) 189: true 190: 191: elsif pres.type == :subscribe 192: @subscription_request_cbs.process(item, pres) 193: true 194: 195: else 196: unless item.nil? 197: update_presence(item, pres) 198: true # Callback consumed stanza 199: else 200: false # Callback did not consume stanza 201: end 202: end 203: end
Update the presence of an item, used internally
Callbacks are called here
# File lib/xmpp4r/roster/helper/roster.rb, line 210 210: def update_presence(item, pres) 211: 212: # This requires special handling, to announce all resources offline 213: if pres.from.resource.nil? and pres.type == :error 214: oldpresences = [] 215: item.each_presence do |oldpres| 216: oldpresences << oldpres 217: end 218: 219: item.add_presence(pres) 220: oldpresences.each { |oldpres| 221: @presence_cbs.process(item, oldpres, pres) 222: } 223: else 224: oldpres = item.presence(pres.from).nil? ? 225: nil : 226: Presence.new.import(item.presence(pres.from)) 227: 228: item.add_presence(pres) 229: @presence_cbs.process(item, oldpres, pres) 230: end 231: end