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: if iq.query.kind_of?(IqQueryRoster) 49: Thread.new do 50: Thread.current.abort_on_exception = true 51: handle_iq_query_roster(iq) 52: end 53: 54: true 55: else 56: false 57: end 58: } 59: stream.add_presence_callback(120, self) { |pres| 60: Thread.new do 61: Thread.current.abort_on_exception = true 62: handle_presence(pres) 63: end 64: } 65: 66: # Request the roster 67: rosterget = Iq.new_rosterget 68: stream.send(rosterget) 69: 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 232 232: def [](jid) 233: jid = JID.new(jid) unless jid.kind_of? JID 234: 235: @items_lock.synchronize { 236: if @items.has_key?(jid) 237: @items[jid] 238: elsif @items.has_key?(jid.strip) 239: @items[jid.strip] 240: else 241: nil 242: end 243: } 244: end
Accept a subscription request
jid: | [JID] of contact |
iname: | [String] Optional roster item name |
# File lib/xmpp4r/roster/helper/roster.rb, line 335 335: def accept_subscription(jid, iname=nil) 336: pres = Presence.new.set_type(:subscribed).set_to(jid.strip) 337: @stream.send(pres) 338: 339: unless self[jid.strip] 340: request = Iq.new_rosterset 341: request.query.add(Jabber::Roster::RosterItem.new(jid.strip, iname)) 342: @stream.send_with_id(request) { true } 343: end 344: 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 311 311: def add(jid, iname=nil, subscribe=false) 312: if self[jid] 313: self[jid].send 314: else 315: request = Iq.new_rosterset 316: request.query.add(Jabber::Roster::RosterItem.new(jid, iname)) 317: @stream.send_with_id(request) { true } 318: # Adding to list is handled by handle_iq_query_roster 319: end 320: 321: if subscribe 322: # Actually the item *should* already be known now, 323: # but we do it manually to exclude conditions. 324: pres = Presence.new.set_type(:subscribe).set_to(jid.strip) 325: @stream.send(pres) 326: end 327: 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 105 105: def add_presence_callback(prio = 0, ref = nil, &block) 106: @presence_cbs.add(prio, ref, block) 107: 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 78 78: def add_query_callback(prio = 0, ref = nil, &block) 79: @query_cbs.add(prio, ref, block) 80: 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 120 120: def add_subscription_callback(prio = 0, ref = nil, &block) 121: @subscription_cbs.add(prio, ref, block) 122: 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 143 143: def add_subscription_request_callback(prio = 0, ref = nil, &block) 144: @subscription_request_cbs.add(prio, ref, block) 145: 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 91 91: def add_update_callback(prio = 0, ref = nil, &block) 92: @update_cbs.add(prio, ref, block) 93: end
Decline a subscription request
# File lib/xmpp4r/roster/helper/roster.rb, line 349 349: def decline_subscription(jid) 350: pres = Presence.new.set_type(:unsubscribed).set_to(jid.strip) 351: @stream.send(pres) 352: 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 249 249: def find(jid) 250: jid = JID.new(jid) unless jid.kind_of? JID 251: 252: j = jid.strip 253: l = {} 254: @items_lock.synchronize { 255: @items.each_pair do |k, v| 256: l[k] = v if k.strip == j 257: end 258: } 259: l 260: 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 285 285: def find_by_group(group) 286: res = [] 287: @items_lock.synchronize { 288: @items.each_pair do |jid,item| 289: res.push(item) if item.groups.include?(group) 290: res.push(item) if item.groups == [] and group.nil? 291: end 292: } 293: res 294: 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 268 268: def groups 269: res = [] 270: @items_lock.synchronize { 271: @items.each_pair do |jid,item| 272: res += item.groups 273: res += [nil] if item.groups == [] 274: end 275: } 276: res.uniq.sort { |a,b| a.to_s <=> b.to_s } 277: end
Handle received <iq/> stanzas, used internally
# File lib/xmpp4r/roster/helper/roster.rb, line 152 152: def handle_iq_query_roster(iq) 153: # If the <iq/> contains <error/> we just ignore that 154: # and assume an empty roster 155: iq.query.each_element('item') do |item| 156: olditem, newitem = nil, nil 157: 158: @items_lock.synchronize { 159: olditem = @items[item.jid] 160: 161: # Handle deletion of item 162: if item.subscription == :remove 163: @items.delete(item.jid) 164: else 165: newitem = @items[item.jid] = RosterItem.new(@stream).import(item) 166: end 167: } 168: @update_cbs.process(olditem, newitem) 169: end 170: 171: @query_cbs.process(iq) 172: end
Handle received <presence/> stanzas, used internally
# File lib/xmpp4r/roster/helper/roster.rb, line 177 177: def handle_presence(pres) 178: item = self[pres.from] 179: 180: if [:subscribed, :unsubscribe, :unsubscribed].include?(pres.type) 181: @subscription_cbs.process(item, pres) 182: true 183: 184: elsif pres.type == :subscribe 185: @subscription_request_cbs.process(item, pres) 186: true 187: 188: else 189: unless item.nil? 190: update_presence(item, pres) 191: true # Callback consumed stanza 192: else 193: false # Callback did not consume stanza 194: end 195: end 196: end
Update the presence of an item, used internally
Callbacks are called here
# File lib/xmpp4r/roster/helper/roster.rb, line 203 203: def update_presence(item, pres) 204: 205: # This requires special handling, to announce all resources offline 206: if pres.from.resource.nil? and pres.type == :error 207: oldpresences = [] 208: item.each_presence do |oldpres| 209: oldpresences << oldpres 210: end 211: 212: item.add_presence(pres) 213: oldpresences.each { |oldpres| 214: @presence_cbs.process(item, oldpres, pres) 215: } 216: else 217: oldpres = item.presence(pres.from).nil? ? 218: nil : 219: Presence.new.import(item.presence(pres.from)) 220: 221: item.add_presence(pres) 222: @presence_cbs.process(item, oldpres, pres) 223: end 224: end