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.
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!)
# File lib/xmpp4r/roster/helper/roster.rb, line 28 28: def initialize(stream) 29: @stream = stream 30: @items = {} 31: @items_lock = Mutex.new 32: @query_cbs = CallbackList.new 33: @update_cbs = CallbackList.new 34: @presence_cbs = CallbackList.new 35: @subscription_cbs = CallbackList.new 36: @subscription_request_cbs = CallbackList.new 37: 38: # Register cbs 39: stream.add_iq_callback(120, self) { |iq| 40: handle_iq(iq) 41: } 42: stream.add_presence_callback(120, self) { |pres| 43: handle_presence(pres) 44: } 45: 46: # Request the roster 47: rosterget = Iq.new_rosterget 48: stream.send(rosterget) 49: 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 224 224: def [](jid) 225: jid = JID.new(jid) unless jid.kind_of? JID 226: 227: @items_lock.synchronize { 228: if @items.has_key?(jid) 229: @items[jid] 230: elsif @items.has_key?(jid.strip) 231: @items[jid.strip] 232: else 233: nil 234: end 235: } 236: end
Accept a subscription request
jid: | [JID] of contact |
iname: | [String] Optional roster item name |
# File lib/xmpp4r/roster/helper/roster.rb, line 327 327: def accept_subscription(jid, iname=nil) 328: pres = Presence.new.set_type(:subscribed).set_to(jid.strip) 329: @stream.send(pres) 330: 331: unless self[jid.strip] 332: request = Iq.new_rosterset 333: request.query.add(Jabber::Roster::RosterItem.new(jid.strip, iname)) 334: @stream.send_with_id(request) { true } 335: end 336: 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 303 303: def add(jid, iname=nil, subscribe=false) 304: if self[jid] 305: self[jid].send 306: else 307: request = Iq.new_rosterset 308: request.query.add(Jabber::Roster::RosterItem.new(jid, iname)) 309: @stream.send_with_id(request) { true } 310: # Adding to list is handled by handle_iq 311: end 312: 313: if subscribe 314: # Actually the item *should* already be known now, 315: # but we do it manually to exclude conditions. 316: pres = Presence.new.set_type(:subscribe).set_to(jid.strip) 317: @stream.send(pres) 318: end 319: 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 85 85: def add_presence_callback(prio = 0, ref = nil, &block) 86: @presence_cbs.add(prio, ref, block) 87: 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 58 58: def add_query_callback(prio = 0, ref = nil, &block) 59: @query_cbs.add(prio, ref, block) 60: 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 100 100: def add_subscription_callback(prio = 0, ref = nil, &block) 101: @subscription_cbs.add(prio, ref, block) 102: 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 123 123: def add_subscription_request_callback(prio = 0, ref = nil, &block) 124: @subscription_request_cbs.add(prio, ref, block) 125: 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 71 71: def add_update_callback(prio = 0, ref = nil, &block) 72: @update_cbs.add(prio, ref, block) 73: end
Decline a subscription request
# File lib/xmpp4r/roster/helper/roster.rb, line 341 341: def decline_subscription(jid) 342: pres = Presence.new.set_type(:unsubscribed).set_to(jid.strip) 343: @stream.send(pres) 344: 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 241 241: def find(jid) 242: jid = JID.new(jid) unless jid.kind_of? JID 243: 244: j = jid.strip 245: l = {} 246: @items_lock.synchronize { 247: @items.each_pair do |k, v| 248: l[k] = v if k.strip == j 249: end 250: } 251: l 252: 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 277 277: def find_by_group(group) 278: res = [] 279: @items_lock.synchronize { 280: @items.each_pair do |jid,item| 281: res.push(item) if item.groups.include?(group) 282: res.push(item) if item.groups == [] and group.nil? 283: end 284: } 285: res 286: 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 260 260: def groups 261: res = [] 262: @items_lock.synchronize { 263: @items.each_pair do |jid,item| 264: res += item.groups 265: res += [nil] if item.groups == [] 266: end 267: } 268: res.uniq.sort { |a,b| a.to_s <=> b.to_s } 269: end
Handle received <iq/> stanzas, used internally
# File lib/xmpp4r/roster/helper/roster.rb, line 132 132: def handle_iq(iq) 133: if iq.query.kind_of?(IqQueryRoster) 134: # If the <iq/> contains <error/> we just ignore that 135: # and assume an empty roster 136: iq.query.each_element('item') do |item| 137: # Handle deletion of item 138: if item.subscription == :remove 139: @items_lock.synchronize { 140: @items.delete(item.jid) 141: } 142: 143: else 144: olditem = nil 145: @items_lock.synchronize { 146: if @items.has_key?(item.jid) 147: olditem = RosterItem.new(@stream).import(@items[item.jid]) 148: 149: # Clear first, because import doesn't 150: @items[item.jid].iname = nil 151: @items[item.jid].subscription = nil 152: @items[item.jid].ask = nil 153: 154: @items[item.jid].import(item) 155: else 156: @items[item.jid] = RosterItem.new(@stream).import(item) 157: end 158: } 159: @update_cbs.process(olditem, @items[item.jid]) 160: end 161: end 162: 163: @query_cbs.process(iq) 164: else 165: false 166: end 167: end
Handle received <presence/> stanzas, used internally
# File lib/xmpp4r/roster/helper/roster.rb, line 172 172: def handle_presence(pres) 173: item = self[pres.from] 174: if [:subscribed, :unsubscribe, :unsubscribed].include?(pres.type) 175: @subscription_cbs.process(item, pres) 176: true 177: elsif pres.type == :subscribe 178: @subscription_request_cbs.process(item, pres) 179: true 180: else 181: unless item.nil? 182: update_presence(item, pres) 183: true # Callback consumed stanza 184: else 185: false # Callback did not consume stanza 186: end 187: end 188: end
Update the presence of an item, used internally
Callbacks are called here
# File lib/xmpp4r/roster/helper/roster.rb, line 195 195: def update_presence(item, pres) 196: 197: # This requires special handling, to announce all resources offline 198: if pres.from.resource.nil? and pres.type == :error 199: oldpresences = [] 200: item.each_presence do |oldpres| 201: oldpresences << oldpres 202: end 203: 204: item.add_presence(pres) 205: oldpresences.each { |oldpres| 206: @presence_cbs.process(item, oldpres, pres) 207: } 208: else 209: oldpres = item.presence(pres.from).nil? ? 210: nil : 211: Presence.new.import(item.presence(pres.from)) 212: 213: item.add_presence(pres) 214: @presence_cbs.process(item, oldpres, pres) 215: end 216: end