| 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