Class: Jabber::Simple

Inherits:
Object
  • Object
show all
Includes:
DRb::DRbUndumped
Defined in:
lib/xmpp4r-simple.rb

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(jid, password, status = nil, status_message = "Available", host = nil, port = 5222, server = nil, register = false) ⇒ Simple

Create a new Jabber::Simple client. You will be automatically connected to the Jabber server and your status message will be set to the string passed in as the status_message argument. If you’d like to connect to a different talk server than the one which would be guessed from your jid, you may provide a server. For example, to connect to the gmail talk servers with a jid that doesn’t end in @gmail.com, just provide ‘talk.l.google.com’ as the server. You may leave server as nil to use the default.

jabber = Jabber::Simple.new(“[email protected]”, “password”, “Chat with me - Please!”)



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/xmpp4r-simple.rb', line 125

def initialize(jid, password, status = nil, status_message = "Available", host = nil, port = 5222, server=nil, register = false)
  @jid = jid
  @password = password
  @host = host
  @port = port
  @disconnected = false
  @server = server
  register!(password) if @register = register
  status(status, status_message)
  start_deferred_delivery_thread
  
  @pubsub = @pubsub_jid = nil
  begin
    domain = Jabber::JID.new(@jid).domain
    @pubsub_jid = "pubsub." + domain
    set_pubsub_service(@pubsub_jid)
  rescue
    @pubsub = @pubsub_jid = nil
  end
end

Class Method Details

.register(jid, password, status = nil, status_message = "Available") ⇒ Object



112
113
114
# File 'lib/xmpp4r-simple.rb', line 112

def self.register(jid, password, status = nil, status_message = "Available")
  new(jid, password, status, status_message, nil, 5222, nil, true)
end

Instance Method Details

#accept_subscriptions=(accept_status) ⇒ Object

Change whether or not subscriptions (friend requests) are automatically accepted.



442
443
444
# File 'lib/xmpp4r-simple.rb', line 442

def accept_subscriptions=(accept_status)
  @accept_subscriptions = accept_status
end

#accept_subscriptions?Boolean

Returns true if auto-accept subscriptions (friend requests) is enabled (default), false otherwise.

Returns:

  • (Boolean)


436
437
438
439
# File 'lib/xmpp4r-simple.rb', line 436

def accept_subscriptions?
  @accept_subscriptions = true if @accept_subscriptions.nil?
  @accept_subscriptions
end

#add(*jids) ⇒ Object

Ask the users specified by jids for authorization (i.e., ask them to add you to their contact list). If you are already in the user’s contact list, add() will not attempt to re-request authorization. In order to force re-authorization, first remove() the user, then re-add them.

Example usage:

jabber_simple.add("[email protected]")

Because the authorization process might take a few seconds, or might never happen depending on when (and if) the user accepts your request, results are placed in the Jabber::Simple#new_subscriptions queue.



233
234
235
236
237
238
# File 'lib/xmpp4r-simple.rb', line 233

def add(*jids)
  contacts(*jids) do |friend|
    next if subscribed_to? friend
    friend.ask_for_authorization!
  end
end

#attempt!Object



484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
# File 'lib/xmpp4r-simple.rb', line 484

def attempt!
  attempts = 0
  begin
    attempts += 1
	yield
  rescue Errno::EPIPE, IOError => e
    sleep 1
    disconnect
    reconnect
    retry unless attempts > 3
    raise e
  rescue Errno::ECONNRESET => e
    sleep (attempts^2) * 60 + 60
    disconnect
    reconnect
    retry unless attempts > 3
    raise e
  end
end

#avatar_hashObject

Returns SHA1 hash of the avatar image data.

This hash is then included in the user’s presence information.



291
292
293
294
295
296
297
298
# File 'lib/xmpp4r-simple.rb', line 291

def avatar_hash
  vcard = get_info
  if !vcard.nil? && !vcard["PHOTO/BINVAL"].nil?
    Digest::SHA1.hexdigest(Base64.decode64(vcard["PHOTO/BINVAL"]))
  else
    nil
  end
end

#capture_iq_stanzas!(capture = true) ⇒ Object

Tell the client to capture (or stop capturing) <iq/> stanzas addressed to it. When the client is initially started, this is false.



356
357
358
# File 'lib/xmpp4r-simple.rb', line 356

def capture_iq_stanzas!(capture=true)
  @iq_mutex.synchronize { @capture_iq_stanzas = capture }
end

#capture_iq_stanzas?Boolean

Returns true if iq stanzas will be captured in the queue, as they arrive, false otherwise.

Returns:

  • (Boolean)


362
363
364
# File 'lib/xmpp4r-simple.rb', line 362

def capture_iq_stanzas?
  @capture_iq_stanzas
end

#clientObject

Direct access to the underlying Jabber client.



453
454
455
456
# File 'lib/xmpp4r-simple.rb', line 453

def client
  connect!() unless connected?
  @client
end

#connected?Boolean

Returns true if the Jabber client is connected to the Jabber server, false otherwise.

Returns:

  • (Boolean)


302
303
304
305
306
# File 'lib/xmpp4r-simple.rb', line 302

def connected?
  @client ||= nil
  connected = @client.respond_to?(:is_connected?) && @client.is_connected?
  return connected
end

#contact_listObject

Returns array of roster items.



256
257
258
# File 'lib/xmpp4r-simple.rb', line 256

def contact_list
  roster.items.values
end

#create_node(node) ⇒ Object

Create a PubSub node (Lots of options still have to be encoded!)

Raises:



596
597
598
599
# File 'lib/xmpp4r-simple.rb', line 596

def create_node(node)
  raise NoPubSubService, "Have you forgot to call #set_pubsub_service ?" if ! has_pubsub?
  @pubsub.create_node(node)
end

#delete_node(node) ⇒ Object

Delete a PubSub node (Lots of options still have to be encoded!)

Raises:



611
612
613
614
# File 'lib/xmpp4r-simple.rb', line 611

def delete_node(node)
  raise NoPubSubService, "Have you forgot to call #set_pubsub_service ?" if ! has_pubsub?
  @pubsub.delete_node(node)
end

#deliver(jids, message, type = :chat, chat_state = :active) ⇒ Object

Send a message to jabber user jid. It is possible to send message to multiple users at once, just supply array of jids.

Example usage:

jabber_simple.deliver("[email protected]", "blabla")
jabber_simple.deliver(["[email protected]", "[email protected]"], "blabla")

Valid message types are:

* :normal (default): a normal message.
* :chat: a one-to-one chat message.
* :groupchat: a group-chat message.
* :headline: a "headline" message.
* :error: an error message.

If the recipient is not in your contacts list, the message will be queued for later delivery, and the Contact will be automatically asked for authorization (see Jabber::Simple#add).

message should be a string or a valid Jabber::Message object. In either case, the message recipient will be set to jid.



173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/xmpp4r-simple.rb', line 173

def deliver(jids, message, type = :chat, chat_state = :active)
  contacts(jids) do |friend|
    unless subscribed_to? friend
      add(friend.jid)
      deliver_deferred(friend.jid, message, type)
      next
    end
    if message.kind_of?(Jabber::Message)
      msg = message
      msg.to = friend.jid
    else
      msg = Message.new(friend.jid)
      msg.type = type
      msg.chat_state = chat_state
      msg.body = message
    end
    send!(msg)
  end
end

#deliver_deferred(jid, message, type) ⇒ Object

Queue messages for delivery once a user has accepted our authorization request. Works in conjunction with the deferred delivery thread.

You can use this method if you want to manually add friends and still have the message queued for later delivery.



521
522
523
524
# File 'lib/xmpp4r-simple.rb', line 521

def deliver_deferred(jid, message, type)
  msg = {:to => jid, :message => message, :type => type}
  queue(:pending_messages) << [msg]
end

#disconnectObject

Use this to force the client to disconnect and not automatically reconnect.



512
513
514
# File 'lib/xmpp4r-simple.rb', line 512

def disconnect
  disconnect!
end

#get_info(jid = nil) ⇒ Object

Retrieve vCard of an entity

Raises exception upon retrieval error, please catch that!

Usage of Threads is suggested here as vCards can be very big.

jid
Jabber::JID

or nil (should be stripped, nil for the client’s own vCard)

result
Jabber::IqVcard

or nil (nil results may be handled as empty vCards)



269
270
271
# File 'lib/xmpp4r-simple.rb', line 269

def get_info(jid = nil)
  Jabber::Vcard::Helper.new(client).get(jid)
end

#has_pubsub?Boolean

Checks if the PubSub service is set

Returns:

  • (Boolean)


527
528
529
# File 'lib/xmpp4r-simple.rb', line 527

def has_pubsub?
  ! @pubsub.nil?
end

#inspectObject

:nodoc:



146
147
148
# File 'lib/xmpp4r-simple.rb', line 146

def inspect #:nodoc:
  "Jabber::Simple #{@jid}"
end

#iq_stanza(&block) ⇒ Object

Returns an array of iq stanzas received since the last time iq_stanzas was called. There will be no stanzas in this queue unless you have enabled capture of <iq/> stanzas using the capture_iq_stanzas! method. Passing a block will yield each stanza in turn, allowing you to break part-way through processing (especially useful when your message handling code is not thread-safe (e.g., ActiveRecord).

e.g.:

jabber.capture_iq_stanzas!
jabber.iq_stanzas do |iq|
  puts "Received iq stanza from #{iq.from}"
end


343
344
345
# File 'lib/xmpp4r-simple.rb', line 343

def iq_stanza(&block)
  dequeue(:iq_stanzas, &block)
end

#iq_stanzas?Boolean

Returns true if there are unprocessed iq stanzas waiting in the queue, false otherwise.

Returns:

  • (Boolean)


349
350
351
# File 'lib/xmpp4r-simple.rb', line 349

def iq_stanzas?
  !queue(:iq_stanzas).empty?
end

#my_nodesObject

Return an array of nodes I own



602
603
604
605
606
607
608
# File 'lib/xmpp4r-simple.rb', line 602

def my_nodes
  ret = []
  pubsubscriptions.each do |sub|
    ret << sub.node if sub.attributes['affiliation'] == 'owner'
  end
  return ret
end

#new_subscriptions(&block) ⇒ Object

Returns an array of subscription notifications received since the last time new_subscriptions was called. Passing a block will yield each update in turn, allowing you to break part-way through processing (especially useful when your subscription handling code is not thread-safe (e.g., ActiveRecord).

e.g.:

jabber.new_subscriptions do |friend, presence|
  puts "Received presence update from #{friend.to_s}: #{presence}"
end


409
410
411
# File 'lib/xmpp4r-simple.rb', line 409

def new_subscriptions(&block)
  dequeue(:new_subscriptions, &block)
end

#new_subscriptions?Boolean

Returns true if there are unprocessed presence updates waiting in the queue, false otherwise.

Returns:

  • (Boolean)


415
416
417
# File 'lib/xmpp4r-simple.rb', line 415

def new_subscriptions?
  !queue(:new_subscriptions).empty?
end

#presence_updates(&block) ⇒ Object

Returns an array of presence updates received since the last time presence_updates was called. Passing a block will yield each update in turn, allowing you to break part-way through processing (especially useful when your presence handling code is not thread-safe (e.g., ActiveRecord).

e.g.:

jabber.presence_updates do |friend, new_presence|
  puts "Received presence update from #{friend}: #{new_presence}"
end


377
378
379
380
381
382
383
384
385
386
387
388
389
390
# File 'lib/xmpp4r-simple.rb', line 377

def presence_updates(&block)
  updates = []
  @presence_mutex.synchronize do
    dequeue(:presence_updates) do |friend|
      presence = @presence_updates[friend]
      next unless presence
      new_update = [friend, presence[0], presence[1]]
      yield new_update if block_given?
      updates << new_update
      @presence_updates.delete(friend)
    end
  end
  return updates
end

#presence_updates?Boolean

Returns true if there are unprocessed presence updates waiting in the queue, false otherwise.

Returns:

  • (Boolean)


394
395
396
# File 'lib/xmpp4r-simple.rb', line 394

def presence_updates?
  !queue(:presence_updates).empty?
end

#publish_atom_item(node, title, body, time = Time.now) ⇒ Object

Publish atom Item. This is an item with one atom entry with title, body and time.

Raises:



634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
# File 'lib/xmpp4r-simple.rb', line 634

def publish_atom_item(node, title, body, time = Time.now)
  raise NoPubSubService, "Have you forgot to call #set_pubsub_service ?" if ! has_pubsub?

  item = Jabber::PubSub::Item.new
  entry = REXML::Element.new('entry')
  entry.add_namespace("http://www.w3.org/2005/Atom")
  mytitle = REXML::Element.new('title')
  mytitle.text = title
  entry.add(mytitle)
  mybody = REXML::Element.new('body')
  mybody.text = body
  entry.add(mybody)
  published = REXML::Element.new("published")
  published.text = time.utc.iso8601
  entry.add(published)
  item.add(entry)
  publish_item(node, item)
end

#publish_item(node, item) ⇒ Object

Publish an Item. This infers an item of Jabber::PubSub::Item kind is passed

Raises:



617
618
619
620
# File 'lib/xmpp4r-simple.rb', line 617

def publish_item(node, item)
  raise NoPubSubService, "Have you forgot to call #set_pubsub_service ?" if ! has_pubsub?
  @pubsub.publish_item_to(node, item)
end

#publish_simple_item(node, text) ⇒ Object

Publish Simple Item. This is an item with one element and some text to it.

Raises:



623
624
625
626
627
628
629
630
631
# File 'lib/xmpp4r-simple.rb', line 623

def publish_simple_item(node, text)
  raise NoPubSubService, "Have you forgot to call #set_pubsub_service ?" if ! has_pubsub?

  item = Jabber::PubSub::Item.new
  xml = REXML::Element.new('value')
  xml.text = text
  item.add(xml)
  publish_item(node, item)
end

#pubsubscribe_to(node) ⇒ Object

Subscribe to a node.

Raises:



544
545
546
547
# File 'lib/xmpp4r-simple.rb', line 544

def pubsubscribe_to(node)
  raise NoPubSubService, "Have you forgot to call #set_pubsub_service ?" if ! has_pubsub?
  @pubsub.subscribe_to(node)
end

#pubsubscriptionsObject

Return the subscriptions we have in the configured PubSub service.

Raises:



579
580
581
582
# File 'lib/xmpp4r-simple.rb', line 579

def pubsubscriptions
  raise NoPubSubService, "Have you forgot to call #set_pubsub_service ?" if ! has_pubsub?
  @pubsub.get_subscriptions_from_all_nodes()
end

#pubunsubscribe_from(node) ⇒ Object

Unsubscribe from a node.

Raises:



550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
# File 'lib/xmpp4r-simple.rb', line 550

def pubunsubscribe_from(node)
  raise NoPubSubService, "Have you forgot to call #set_pubsub_service ?" if ! has_pubsub?

  # FIXME
  # @pubsub.unsubscribe_from(node)
  # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  # The above should just work, but I had to reimplement it since XMPP4R doesn't support subids
  # and OpenFire (the Jabber Server I am testing against) seems to require it.

  subids = find_subids_for(node)
  return if subids.empty?

  subids.each do |subid|
    iq = Jabber::Iq.new(:set, @pubsub_jid)
    iq.add(Jabber::PubSub::IqPubSub.new)
    iq.from = @jid
    unsub = REXML::Element.new('unsubscribe')
    unsub.attributes['node'] = node
    unsub.attributes['jid'] = @jid
    unsub.attributes['subid'] = subid
    iq.pubsub.add(unsub)
    res = nil
    @client.send_with_id(iq) do |reply|
      res = reply.kind_of?(Jabber::Iq) and reply.type == :result
    end # @stream.send_with_id(iq)
  end
end

#received_events(&block) ⇒ Object

Just like #received_messages, but for PubSub events



585
586
587
# File 'lib/xmpp4r-simple.rb', line 585

def received_events(&block)
  dequeue(:received_events, &block)
end

#received_events?Boolean

Returns true if there are unprocessed received events waiting in the queue, false otherwise.

Returns:

  • (Boolean)


591
592
593
# File 'lib/xmpp4r-simple.rb', line 591

def received_events?
  !queue(:received_events).empty?
end

#received_messages(&block) ⇒ Object

Returns an array of messages received since the last time received_messages was called. Passing a block will yield each message in turn, allowing you to break part-way through processing (especially useful when your message handling code is not thread-safe (e.g., ActiveRecord).

e.g.:

jabber.received_messages do |message|
  puts "Received message from #{message.from}: #{message.body}"
end


319
320
321
# File 'lib/xmpp4r-simple.rb', line 319

def received_messages(&block)
  dequeue(:received_messages, &block)
end

#received_messages?Boolean

Returns true if there are unprocessed received messages waiting in the queue, false otherwise.

Returns:

  • (Boolean)


325
326
327
# File 'lib/xmpp4r-simple.rb', line 325

def received_messages?
  !queue(:received_messages).empty?
end

#reconnectObject

Use this to force the client to reconnect after a force_disconnect.



505
506
507
508
# File 'lib/xmpp4r-simple.rb', line 505

def reconnect
  @disconnected = false
  connect!
end

#register!(password) ⇒ Object



457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
# File 'lib/xmpp4r-simple.rb', line 457

def register!(password)
  attempt! {
    begin
      client.register(password)
      @register = false
      disconnect
      reconnect
    rescue Exception => e
      error_msg = "Error registering: #{e.message}\n\n"
      if e.respond_to?('error') && e.error.type == :modify
        error_msg += "Accepted registration information:\n"
        instructions, fields = client.register_info
        fields.each { |info|
          error_msg += "* #{info}\n"
        }
        error_msg += "(#{instructions})"
      end
      raise RegistrationError, error_msg
    end
  }
end

#remove(*jids) ⇒ Object

Remove the jabber users specified by jids from the contact list.



241
242
243
244
245
# File 'lib/xmpp4r-simple.rb', line 241

def remove(*jids)
  contacts(*jids) do |unfriend|
    unfriend.unsubscribe!
  end
end

#rosterObject

Direct access to the underlying Roster helper.



447
448
449
450
# File 'lib/xmpp4r-simple.rb', line 447

def roster
  return @roster if @roster
  self.roster = Roster::Helper.new(client)
end

#send!(msg) ⇒ Object

Send a Jabber stanza over-the-wire.



479
480
481
482
483
# File 'lib/xmpp4r-simple.rb', line 479

def send!(msg)
	attempt! {
	client.send(msg)
	}
end

#set_pubsub_service(service) ⇒ Object

Sets the PubSub service. Just one service is allowed.

Raises:



532
533
534
535
536
537
538
539
540
541
# File 'lib/xmpp4r-simple.rb', line 532

def set_pubsub_service(service)
  raise NotConnected, "You are not connected" if @disconnected
  raise AlreadySet, "You already have a PubSub service. Currently it's not allowed to have more." if has_pubsub?
  @pubsub = PubSub::ServiceHelper.new(@client, service)
  @pubsub_jid = service

  @pubsub.add_event_callback do |event|
    queue(:received_events) << event
  end
end

#status(presence, message) ⇒ Object

Set your presence, with a message.

Available values for presence are:

* nil: online.
* :chat: free for chat.
* :away: away from the computer.
* :dnd: do not disturb.
* :xa: extended away.

It’s not possible to set an offline status - to do that, disconnect! :-)



204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
# File 'lib/xmpp4r-simple.rb', line 204

def status(presence, message)
  @presence = presence
  @status_message = message
  stat_msg = Presence.new(@presence, @status_message)

  if !avatar_hash.nil?
    x = Jabber::X.new
    x.add_namespace('vcard-temp:x:update')
    photo = REXML::Element::new("photo")
    photo.add(REXML::Text.new(avatar_hash))
    x.add(photo)
    stat_msg.add_element(x)
  end
  
  send!(stat_msg)
end

#subscribed_to?(jid) ⇒ Boolean

Returns true if this Jabber account is subscribed to status updates for the jabber user jid, false otherwise.

Returns:

  • (Boolean)


249
250
251
252
253
# File 'lib/xmpp4r-simple.rb', line 249

def subscribed_to?(jid)
  contacts(jid) do |contact|
    return contact.subscribed?
  end
end

#subscription_requests(&block) ⇒ Object

Returns an array of subscription notifications received since the last time subscription_requests was called. Passing a block will yield each update in turn, allowing you to break part-way through processing (especially useful when your subscription handling code is not thread-safe (e.g., ActiveRecord).

e.g.:

jabber.subscription_requests do |friend, presence|
  puts "Received presence update from #{friend.to_s}: #{presence}"
end


430
431
432
# File 'lib/xmpp4r-simple.rb', line 430

def subscription_requests(&block)
  dequeue(:subscription_requests, &block)
end

#update_info(vcard) ⇒ Object

Update your own vCard

Raises exception when setting fails

Usage of Threads suggested here, too. The function waits for approval from the server. e.g.:

info =  Jabber::Vcard::IqVcard.new
info["NICKNAME"] = "amay"
jabber.update_info(info)


284
285
286
# File 'lib/xmpp4r-simple.rb', line 284

def update_info(vcard)
  Jabber::Vcard::Helper.new(client).set(vcard)
end