| 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