xmpp4r 0.5 → 0.5.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +21 -0
- data/README.rdoc +29 -38
- data/Rakefile +11 -18
- data/data/doc/xmpp4r/examples/advanced/recvfile.rb +5 -4
- data/lib/xmpp4r/bytestreams/helper/filetransfer.rb +2 -0
- data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/base.rb +1 -0
- data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/initiator.rb +1 -1
- data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/target.rb +4 -1
- data/lib/xmpp4r/caps/helper/generator.rb +2 -2
- data/lib/xmpp4r/client.rb +18 -4
- data/lib/xmpp4r/connection.rb +8 -5
- data/lib/xmpp4r/delay/x/delay.rb +1 -1
- data/lib/xmpp4r/entity_time.rb +6 -0
- data/lib/xmpp4r/entity_time/iq.rb +45 -0
- data/lib/xmpp4r/entity_time/responder.rb +57 -0
- data/lib/xmpp4r/httpbinding/client.rb +178 -44
- data/lib/xmpp4r/message.rb +46 -0
- data/lib/xmpp4r/muc/helper/mucclient.rb +8 -1
- data/lib/xmpp4r/observable.rb +9 -0
- data/lib/xmpp4r/observable/contact.rb +61 -0
- data/lib/xmpp4r/observable/helper.rb +389 -0
- data/lib/xmpp4r/observable/observable_thing.rb +191 -0
- data/lib/xmpp4r/observable/pubsub.rb +211 -0
- data/lib/xmpp4r/observable/subscription.rb +57 -0
- data/lib/xmpp4r/observable/thread_store.rb +65 -0
- data/lib/xmpp4r/pubsub/children/unsubscribe.rb +16 -1
- data/lib/xmpp4r/pubsub/helper/servicehelper.rb +48 -14
- data/lib/xmpp4r/rexmladdons.rb +46 -6
- data/lib/xmpp4r/roster/helper/roster.rb +19 -9
- data/lib/xmpp4r/sasl.rb +1 -1
- data/lib/xmpp4r/stream.rb +2 -2
- data/lib/xmpp4r/xmpp4r.rb +1 -1
- data/test/bytestreams/tc_socks5bytestreams.rb +2 -2
- data/test/entity_time/tc_responder.rb +65 -0
- data/test/roster/tc_helper.rb +2 -3
- data/test/tc_message.rb +52 -1
- data/test/tc_stream.rb +2 -2
- data/test/tc_streamComponent.rb +11 -1
- data/test/ts_xmpp4r.rb +11 -2
- data/xmpp4r.gemspec +20 -9
- metadata +41 -35
data/lib/xmpp4r/message.rb
CHANGED
@@ -100,6 +100,52 @@ module Jabber
|
|
100
100
|
self
|
101
101
|
end
|
102
102
|
|
103
|
+
##
|
104
|
+
# Returns the message's xhtml body, or nil.
|
105
|
+
# This is the message's xhtml-text content.
|
106
|
+
def xhtml_body
|
107
|
+
html = first_element('html', 'http://jabber.org/protocol/xhtml-im')
|
108
|
+
|
109
|
+
if html
|
110
|
+
html.first_element_content('body', 'http://www.w3.org/1999/xhtml')
|
111
|
+
else
|
112
|
+
first_element_content('body', 'http://www.w3.org/1999/xhtml')
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
##
|
117
|
+
# Sets the message's xhtml body
|
118
|
+
#
|
119
|
+
# b:: [String] xhtml body to set (Note: must be a valid xhtml)
|
120
|
+
def xhtml_body=(b)
|
121
|
+
begin
|
122
|
+
b = REXML::Document.new("<root>#{b}</root>")
|
123
|
+
rescue REXML::ParseException
|
124
|
+
raise ArgumentError, "Body is not a valid xhtml. Have you forgot to close some tag?"
|
125
|
+
end
|
126
|
+
|
127
|
+
html = first_element('html', 'http://jabber.org/protocol/xhtml-im')
|
128
|
+
|
129
|
+
if html
|
130
|
+
html.replace_element_content('body', b, 'http://www.w3.org/1999/xhtml')
|
131
|
+
else
|
132
|
+
el = REXML::Element.new('html')
|
133
|
+
el.add_namespace('http://jabber.org/protocol/xhtml-im')
|
134
|
+
el.replace_element_content('body', b, 'http://www.w3.org/1999/xhtml')
|
135
|
+
add_element(el)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
##
|
140
|
+
# Sets the message's xhtml body
|
141
|
+
#
|
142
|
+
# b:: [String] xhtml body to set (Note: must be a valid xhtml)
|
143
|
+
# return:: [REXML::Element] self for chaining
|
144
|
+
def set_xhtml_body(b)
|
145
|
+
self.xhtml_body = b
|
146
|
+
self
|
147
|
+
end
|
148
|
+
|
103
149
|
##
|
104
150
|
# sets the message's subject
|
105
151
|
#
|
@@ -68,8 +68,9 @@ module Jabber
|
|
68
68
|
# fails.
|
69
69
|
# jid:: [JID] room@component/nick
|
70
70
|
# password:: [String] Optional password
|
71
|
+
# opts:: [Hash] If the parameter :history => false is passed then you will receive no history messages after initial presence
|
71
72
|
# return:: [MUCClient] self (chain-able)
|
72
|
-
def join(jid, password=nil)
|
73
|
+
def join(jid, password=nil, opts={})
|
73
74
|
if active?
|
74
75
|
raise "MUCClient already active"
|
75
76
|
end
|
@@ -83,6 +84,12 @@ module Jabber
|
|
83
84
|
pres.from = @my_jid
|
84
85
|
xmuc = XMUC.new
|
85
86
|
xmuc.password = password
|
87
|
+
|
88
|
+
if !opts[:history]
|
89
|
+
history = REXML::Element.new( 'history').tap {|h| h.add_attribute('maxstanzas','0') }
|
90
|
+
xmuc.add_element history
|
91
|
+
end
|
92
|
+
|
86
93
|
pres.add(xmuc)
|
87
94
|
|
88
95
|
# We don't use Stream#send_with_id here as it's unknown
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# =XMPP4R - XMPP Library for Ruby
|
2
|
+
#
|
3
|
+
# This file's copyright (c) 2009 by Pablo Lorenzzoni <pablo@propus.com.br>
|
4
|
+
# License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option.
|
5
|
+
# Website::http://home.gna.org/xmpp4r/
|
6
|
+
|
7
|
+
require 'xmpp4r/observable/thread_store.rb'
|
8
|
+
require 'xmpp4r/observable/observable_thing.rb'
|
9
|
+
require 'xmpp4r/observable/helper.rb'
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# =XMPP4R - XMPP Library for Ruby
|
2
|
+
#
|
3
|
+
# This file's copyright (c) 2009 by Pablo Lorenzzoni <pablo@propus.com.br>
|
4
|
+
# License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option.
|
5
|
+
# Website::http://home.gna.org/xmpp4r/
|
6
|
+
#
|
7
|
+
module Jabber
|
8
|
+
class Observable
|
9
|
+
# Jabber::Observable::Contact - Convenience subclass to deal with Contacts
|
10
|
+
class Contact
|
11
|
+
|
12
|
+
# Creates a new Jabber::Observable::Contact
|
13
|
+
#
|
14
|
+
# jid:: jid to be used
|
15
|
+
# observable:: observable to be used
|
16
|
+
def initialize(jid, observable)
|
17
|
+
@jid = jid.respond_to?(:resource) ? jid : JID.new(jid)
|
18
|
+
@observable = observable
|
19
|
+
end
|
20
|
+
|
21
|
+
# Returns the stripped version of the JID
|
22
|
+
def jid; @jid.strip; end
|
23
|
+
|
24
|
+
def inspect #:nodoc:
|
25
|
+
sprintf("#<%s:0x%x @jid=%s>", self.class.name, __id__, @jid.to_s)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Are e subscribed?
|
29
|
+
def subscribed?
|
30
|
+
[:to, :both].include?(subscription)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Get the subscription from the roster_item
|
34
|
+
def subscription
|
35
|
+
items = @observable.roster.items
|
36
|
+
return false unless items.include?(jid)
|
37
|
+
items[jid].subscription
|
38
|
+
end
|
39
|
+
|
40
|
+
# Send a request asking for authorization
|
41
|
+
def ask_for_authorization!
|
42
|
+
request!(:subscribe)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Sends a request asking for unsubscription
|
46
|
+
def unsubscribe!
|
47
|
+
request!(:unsubscribe)
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
# Really send the request.
|
53
|
+
def request!(type)
|
54
|
+
request = Jabber::Presence.new.set_type(type)
|
55
|
+
request.to = jid
|
56
|
+
@observable.send!(request)
|
57
|
+
end
|
58
|
+
|
59
|
+
end # of class Contact
|
60
|
+
end # of class Observable
|
61
|
+
end # of module Jabber
|
@@ -0,0 +1,389 @@
|
|
1
|
+
# =XMPP4R - XMPP Library for Ruby
|
2
|
+
#
|
3
|
+
# This file's copyright (c) 2009 by Pablo Lorenzzoni <pablo@propus.com.br>
|
4
|
+
# License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option.
|
5
|
+
# Website::http://home.gna.org/xmpp4r/
|
6
|
+
#
|
7
|
+
# This is the helper for xmpp4r/observable, and was based on XMPP4R-Observable
|
8
|
+
# by Pablo Lorenzoni <pablo@propus.com.br>.
|
9
|
+
require 'time'
|
10
|
+
require 'xmpp4r'
|
11
|
+
require 'xmpp4r/roster'
|
12
|
+
require 'xmpp4r/observable/contact'
|
13
|
+
require 'xmpp4r/observable/pubsub'
|
14
|
+
require 'xmpp4r/observable/subscription'
|
15
|
+
module Jabber
|
16
|
+
|
17
|
+
class NotConnected < StandardError; end #:nodoc:
|
18
|
+
|
19
|
+
# Jabber::Observable - Creates observable Jabber Clients
|
20
|
+
class Observable
|
21
|
+
|
22
|
+
# This is what actually makes our object Observable.
|
23
|
+
include ObservableThing
|
24
|
+
|
25
|
+
attr_reader :subs, :pubsub, :jid, :auto
|
26
|
+
|
27
|
+
# Create a new Jabber::Observable client. You will be automatically connected
|
28
|
+
# to the Jabber server and your status message will be set to the string
|
29
|
+
# passed in as the status_message argument.
|
30
|
+
#
|
31
|
+
# jabber = Jabber::Observable.new("me@example.com", "password", nil, "Talk to me - Please!")
|
32
|
+
#
|
33
|
+
# jid:: your jid (either a string or a JID object)
|
34
|
+
# password:: your password
|
35
|
+
# status:: your status. Check Jabber::Observable#status for documentation
|
36
|
+
# status_message:: some funny string
|
37
|
+
# host:: the server host (if different from the one in the jid)
|
38
|
+
# port:: the server port (default: 5222)
|
39
|
+
def initialize(jid, password, status=nil, status_message="Available", host=nil, port=5222)
|
40
|
+
|
41
|
+
# Basic stuff
|
42
|
+
@jid = jid.respond_to?(:resource) ? jid : Jabber::JID.new(jid)
|
43
|
+
@password = password
|
44
|
+
@host = host
|
45
|
+
@port = port
|
46
|
+
|
47
|
+
# Message dealing
|
48
|
+
@delivered_messages = 0
|
49
|
+
@deferred_messages = Queue.new
|
50
|
+
start_deferred_delivery_thread
|
51
|
+
|
52
|
+
# Connection stuff
|
53
|
+
@connect_mutex = Mutex.new
|
54
|
+
@client = nil
|
55
|
+
@roster = nil
|
56
|
+
@disconnected = false
|
57
|
+
|
58
|
+
# Tell everybody I am here
|
59
|
+
status(status, status_message)
|
60
|
+
|
61
|
+
# Subscription Accessor
|
62
|
+
@subs = Jabber::Observable::Subscriptions.new(self)
|
63
|
+
|
64
|
+
# PubSub Accessor
|
65
|
+
@pubsub = Jabber::Observable::PubSub.new(self)
|
66
|
+
|
67
|
+
# Auto Observer placeholder
|
68
|
+
@auto = nil
|
69
|
+
|
70
|
+
# Our contacts Hash
|
71
|
+
@contacts = Hash.new
|
72
|
+
end
|
73
|
+
|
74
|
+
def inspect # :nodoc:
|
75
|
+
sprintf("#<%s:0x%x @jid=%s, @delivered_messages=%d, @deferred_messages=%d, @observer_count=%s, @notification_count=%s, @pubsub=%s>", self.class.name, __id__, @jid, @delivered_messages, @deferred_messages.length, observer_count.inspect, notification_count.inspect, @pubsub.inspect)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Count the registered observers in each thing
|
79
|
+
def observer_count
|
80
|
+
h = {}
|
81
|
+
[ :message, :presence, :iq, :new_subscription, :subscription_request, :event ].each do |thing|
|
82
|
+
h[thing] = count_observers(thing)
|
83
|
+
end
|
84
|
+
h
|
85
|
+
end
|
86
|
+
|
87
|
+
# Count the notifications really sent for each thing
|
88
|
+
def notification_count
|
89
|
+
h = {}
|
90
|
+
[ :message, :presence, :iq, :new_subscription, :subscription_request, :event ].each do |thing|
|
91
|
+
h[thing] = count_notifications(thing)
|
92
|
+
end
|
93
|
+
h
|
94
|
+
end
|
95
|
+
|
96
|
+
# Attach an auto-observer based on QObserver
|
97
|
+
def attach_auto_observer
|
98
|
+
raise StandardError, "Already attached." unless @auto.nil?
|
99
|
+
|
100
|
+
@auto = QObserver.new
|
101
|
+
[ :message, :presence, :iq, :new_subscription, :subscription_request, :event ].each do |thing|
|
102
|
+
self.add_observer(thing, @auto)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Dettach the auto-observer
|
107
|
+
def dettach_auto_observer
|
108
|
+
raise StandardError, "Not attached." if @auto.nil?
|
109
|
+
|
110
|
+
[ :message, :presence, :iq, :new_subscription, :subscription_request, :event ].each do |thing|
|
111
|
+
self.delete_observer(thing, @auto)
|
112
|
+
end
|
113
|
+
@auto = nil
|
114
|
+
end
|
115
|
+
|
116
|
+
# Send a message to jabber user jid.
|
117
|
+
#
|
118
|
+
# jid:: jid of the recipient
|
119
|
+
# message:: what is to be delivered (either a string or a Jabber::Message)
|
120
|
+
# type:: can be either one of:
|
121
|
+
# * :normal: a normal message.
|
122
|
+
# * :chat (default): a one-to-one chat message.
|
123
|
+
# * :groupchat: a group-chat message.
|
124
|
+
# * :headline: a "headline" message.
|
125
|
+
# * :error: an error message.
|
126
|
+
#
|
127
|
+
# If the recipient is not in your contacts list, the message will be queued
|
128
|
+
# for later delivery, and the Contact will be automatically asked for
|
129
|
+
# authorization (see Jabber::Observable#add).
|
130
|
+
def deliver(jid, message, type=nil)
|
131
|
+
contacts(jid).each do |contact|
|
132
|
+
# Check if we're subscribed to contact
|
133
|
+
if @subs.subscribed_to?(contact)
|
134
|
+
# Yes! we are!
|
135
|
+
if message.instance_of?(Jabber::Message)
|
136
|
+
msg = message
|
137
|
+
msg.to = contact.jid
|
138
|
+
msg.type = type unless type.nil? # Let's keep the Jabber::Message type unless passed
|
139
|
+
else
|
140
|
+
msg = Jabber::Message.new(contact.jid)
|
141
|
+
msg.body = message
|
142
|
+
msg.type = type.nil? ? :chat : type
|
143
|
+
end
|
144
|
+
@delivered_messages += 1
|
145
|
+
send!(msg)
|
146
|
+
else
|
147
|
+
# No... Let's add it and defer the delivery.
|
148
|
+
@subs.add(contact.jid)
|
149
|
+
deliver_deferred(contact.jid, message, type)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
# Set your presence, with a message.
|
155
|
+
#
|
156
|
+
# presence:: any of these:
|
157
|
+
# * nil: online.
|
158
|
+
# * :chat: free for chat.
|
159
|
+
# * :away: away from the computer.
|
160
|
+
# * :dnd: do not disturb.
|
161
|
+
# * :xa: extended away.
|
162
|
+
# message:: a string that you wish your contacts see when you change your presence.
|
163
|
+
def status(presence, message)
|
164
|
+
@status_message = message
|
165
|
+
@presence = presence
|
166
|
+
send!(Jabber::Presence.new(@presence, @status_message))
|
167
|
+
end
|
168
|
+
|
169
|
+
# Transform a passed list of contacts in one or more Jabber::Observable::Contact objects.
|
170
|
+
#
|
171
|
+
# contact:: one of more jids of contacts
|
172
|
+
def contacts(*contact)
|
173
|
+
ret = []
|
174
|
+
contact.each do |c|
|
175
|
+
jid = c.to_s
|
176
|
+
# Do we already have it?
|
177
|
+
if ! @contacts.include?(jid)
|
178
|
+
# Nope.
|
179
|
+
@contacts[jid] = c.instance_of?(Jabber::Observable::Contact) ? c : Jabber::Observable::Contact.new(c, self)
|
180
|
+
end
|
181
|
+
ret << @contacts[jid]
|
182
|
+
end
|
183
|
+
ret
|
184
|
+
end
|
185
|
+
|
186
|
+
# Returns true if the Jabber client is connected to the Jabber server,
|
187
|
+
# false otherwise.
|
188
|
+
def connected?
|
189
|
+
@client.respond_to?(:is_connected?) && @client.is_connected?
|
190
|
+
end
|
191
|
+
|
192
|
+
# Pass the underlying Roster helper.
|
193
|
+
def roster
|
194
|
+
return @roster unless @roster.nil?
|
195
|
+
@roster = Jabber::Roster::Helper.new(client)
|
196
|
+
end
|
197
|
+
|
198
|
+
# Pass the underlying Jabber client.
|
199
|
+
def client
|
200
|
+
connect! unless connected?
|
201
|
+
@client
|
202
|
+
end
|
203
|
+
|
204
|
+
# Send a Jabber stanza over-the-wire.
|
205
|
+
#
|
206
|
+
# msg:: the stanza to be sent.
|
207
|
+
def send!(msg)
|
208
|
+
retries = 0
|
209
|
+
max = 4
|
210
|
+
begin
|
211
|
+
retries += 1
|
212
|
+
client.send(msg)
|
213
|
+
rescue Errno::ECONNRESET => e
|
214
|
+
# Connection reset. Sleep progressively and retry until success or exhaustion.
|
215
|
+
sleep ((retries^2) * 60) + 30
|
216
|
+
disconnect
|
217
|
+
reconnect
|
218
|
+
retry unless retries > max
|
219
|
+
raise e
|
220
|
+
rescue Errno::EPIPE, IOError => e
|
221
|
+
# Some minor error. Sleep 2 seconds and retry until success or exhaustion.
|
222
|
+
sleep 2
|
223
|
+
disconnect
|
224
|
+
reconnect
|
225
|
+
retry unless retries > max
|
226
|
+
raise e
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
# Use this to force the client to reconnect after a disconnect.
|
231
|
+
def reconnect
|
232
|
+
@disconnected = false
|
233
|
+
connect!
|
234
|
+
end
|
235
|
+
|
236
|
+
# Use this to force the client to disconnect and not automatically
|
237
|
+
# reconnect.
|
238
|
+
def disconnect
|
239
|
+
disconnect!(false)
|
240
|
+
end
|
241
|
+
|
242
|
+
# Queue messages for delivery once a user has accepted our authorization
|
243
|
+
# request. Works in conjunction with the deferred delivery thread.
|
244
|
+
#
|
245
|
+
# You can use this method if you want to manually add friends and still
|
246
|
+
# have the message queued for later delivery.
|
247
|
+
def deliver_deferred(jid, message, type)
|
248
|
+
msg = {:to => jid, :message => message, :type => type, :time => Time.now}
|
249
|
+
@deferred_messages.enq msg
|
250
|
+
end
|
251
|
+
|
252
|
+
# Sets the maximum time to wait for a message to be delivered (in
|
253
|
+
# seconds). It will be removed of the queue afterwards.
|
254
|
+
def deferred_max_wait=(seconds)
|
255
|
+
@deferred_max_wait = seconds
|
256
|
+
end
|
257
|
+
|
258
|
+
# Get the maximum time to wait for a message to be delivered. Default: 600
|
259
|
+
# seconds (10 minutes).
|
260
|
+
def deferred_max_wait
|
261
|
+
@deferred_max_wait || 600
|
262
|
+
end
|
263
|
+
|
264
|
+
private
|
265
|
+
|
266
|
+
# Create the infrastructure for connection and do it.
|
267
|
+
#
|
268
|
+
# Note that we use a Mutex to prevent double connection attempts and will
|
269
|
+
# raise a SecurityError if that happens.
|
270
|
+
def connect!
|
271
|
+
raise RuntimeError, "Connections disabled - use Jabber::Observable::reconnect() to reconnect." if @disconnected
|
272
|
+
raise SecurityError, "Connection attempt while already trying to connect!" if @connect_mutex.locked?
|
273
|
+
|
274
|
+
@connect_mutex.synchronize do
|
275
|
+
# Assure we're not connected.
|
276
|
+
disconnect!(false) if connected?
|
277
|
+
|
278
|
+
# Connection
|
279
|
+
jid = Jabber::JID.new(@jid)
|
280
|
+
my_client = Jabber::Client.new(jid)
|
281
|
+
my_client.connect(@host, @port)
|
282
|
+
my_client.auth(@password)
|
283
|
+
@roster = nil
|
284
|
+
@client = my_client
|
285
|
+
|
286
|
+
# Post-connect
|
287
|
+
register_default_callbacks
|
288
|
+
status(@presence, @status_message)
|
289
|
+
if @pubsub.nil?
|
290
|
+
@pubsub = Jabber::Observable::PubSub.new(self)
|
291
|
+
else
|
292
|
+
@pubsub.attach!
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
# Really disconnect the client
|
298
|
+
#
|
299
|
+
# auto_reconnect:: Sets the flag for auto-reconnection
|
300
|
+
def disconnect!(auto_reconnect = true)
|
301
|
+
if connected?
|
302
|
+
client.close
|
303
|
+
end
|
304
|
+
@roster = nil
|
305
|
+
@client = nil
|
306
|
+
@pubsub.set_service(nil)
|
307
|
+
@disconnected = auto_reconnect
|
308
|
+
end
|
309
|
+
|
310
|
+
# This will register callbacks for every "thing" we made observable.
|
311
|
+
#
|
312
|
+
# The observable things we register here are :message, :presence, :iq,
|
313
|
+
# :new_subscription, and :subscription_request
|
314
|
+
#
|
315
|
+
# Note we can also observe :event, but that is dealt with in
|
316
|
+
# Jabber::Observable::PubSub
|
317
|
+
def register_default_callbacks
|
318
|
+
|
319
|
+
# The three basic "things": :message, :presence and :iq
|
320
|
+
# (note that :presence is based on roster)
|
321
|
+
client.add_message_callback do |message|
|
322
|
+
unless message.body.nil?
|
323
|
+
changed(:message)
|
324
|
+
notify_observers(:message, message)
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
roster.add_presence_callback do |roster_item, old_presence, new_presence|
|
329
|
+
simple_jid = roster_item.jid.strip.to_s
|
330
|
+
presence = case new_presence.type
|
331
|
+
when nil then new_presence.show || :online
|
332
|
+
when :unavailable then :unavailable
|
333
|
+
else
|
334
|
+
nil
|
335
|
+
end
|
336
|
+
|
337
|
+
changed(:presence)
|
338
|
+
notify_observers(:presence, simple_jid, presence, new_presence)
|
339
|
+
end
|
340
|
+
|
341
|
+
client.add_iq_callback do |iq|
|
342
|
+
changed(:iq)
|
343
|
+
notify_observers(:iq, iq)
|
344
|
+
end
|
345
|
+
|
346
|
+
# We'll also expose roster's :new_subscription and :subscription_request
|
347
|
+
roster.add_subscription_callback do |roster_item, presence|
|
348
|
+
if presence.type == :subscribed
|
349
|
+
changed(:new_subscription)
|
350
|
+
notify_observers(:new_subscription, [roster_item, presence])
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
roster.add_subscription_request_callback do |roster_item, presence|
|
355
|
+
roster.accept_subscription(presence.from) if @subs.accept?
|
356
|
+
changed(:subscription_request)
|
357
|
+
notify_observers(:subscription_request, [roster_item, presence])
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
# Starts the deferred delivery thread
|
362
|
+
#
|
363
|
+
# This will monitor the @deferred_messages queue and try to deliver messages.
|
364
|
+
def start_deferred_delivery_thread
|
365
|
+
return if ! @deferred_delivery_thread.nil? and @deferred_delivery_thread.alive?
|
366
|
+
|
367
|
+
@deferred_delivery_thread = Thread.new do
|
368
|
+
loop do
|
369
|
+
# Check the queue every 10 seconds. Effectivelly block if nothing is queued.
|
370
|
+
sleep 10 while @deferred_messages.empty?
|
371
|
+
|
372
|
+
# Hm... something has been queued
|
373
|
+
message = @deferred_messages.deq
|
374
|
+
if @subs.subscribed_to?(message[:to])
|
375
|
+
# Great! We're subscribed!
|
376
|
+
deliver(message[:to], message[:message], message[:type])
|
377
|
+
else
|
378
|
+
# Still not subscribed. Enqueue it again (unless deferred_max_wait was reached)
|
379
|
+
@deferred_messages.enq message unless Time.now > (deferred_max_wait + message[:time])
|
380
|
+
end
|
381
|
+
|
382
|
+
# Wait 5 seconds between every message still in the queue
|
383
|
+
sleep 5
|
384
|
+
end
|
385
|
+
end
|
386
|
+
end
|
387
|
+
end # of class Observable
|
388
|
+
end # of module Jabber
|
389
|
+
|