Class: Blather::Stanza::Message

Inherits:
Blather::Stanza show all
Defined in:
lib/blather/stanza/message.rb,
lib/blather/stanza/message/muc_user.rb

Overview

# Message Stanza

[RFC 3921 Section 2.1 - Message Syntax](xmpp.org/rfcs/rfc3921.html#rfc.section.2.1)

Exchanging messages is a basic use of XMPP and occurs when a user generates a message stanza that is addressed to another entity. The sender’s server is responsible for delivering the message to the intended recipient (if the recipient is on the same local server) or for routing the message to the recipient’s server (if the recipient is on a remote server). Thus a message stanza is used to “push” information to another entity.

## “To” Attribute

An instant messaging client specifies an intended recipient for a message by providing the JID of an entity other than the sender in the ‘to` attribute of the Message stanza. If the message is being sent outside the context of any existing chat session or received message, the value of the `to` address SHOULD be of the form “user@domain” rather than of the form “user@domain/resource”.

msg = Message.new '[email protected]/resource'
msg.to == '[email protected]/resource'

msg.to = '[email protected]/resource'
msg.to == '[email protected]/resource'

The ‘to` attribute on a Message stanza works like any regular ruby object attribute

## “Type” Attribute

Common uses of the message stanza in instant messaging applications include: single messages; messages sent in the context of a one-to-one chat session; messages sent in the context of a multi-user chat room; alerts, notifications, or other information to which no reply is expected; and errors. These uses are differentiated via the ‘type` attribute. If included, the `type` attribute MUST have one of the following values:

  • ‘:chat` – The message is sent in the context of a one-to-one chat session. Typically a receiving client will present message of type `chat` in an interface that enables one-to-one chat between the two parties, including an appropriate conversation history.

  • ‘:error` – The message is generated by an entity that experiences an error in processing a message received from another entity. A client that receives a message of type `error` SHOULD present an appropriate interface informing the sender of the nature of the error.

  • ‘:groupchat` – The message is sent in the context of a multi-user chat environment (similar to that of [IRC]). Typically a receiving client will present a message of type `groupchat` in an interface that enables many-to-many chat between the parties, including a roster of parties in the chatroom and an appropriate conversation history.

  • ‘:headline` – The message provides an alert, a notification, or other information to which no reply is expected (e.g., news headlines, sports updates, near-real-time market data, and syndicated content). Because no reply to the message is expected, typically a receiving client will present a message of type “headline” in an interface that appropriately differentiates the message from standalone messages, chat messages, or groupchat messages (e.g., by not providing the recipient with the ability to reply).

  • ‘:normal` – The message is a standalone message that is sent outside the context of a one-to-one conversation or groupchat, and to which it is expected that the recipient will reply. Typically a receiving client will present a message of type `normal` in an interface that enables the recipient to reply, but without a conversation history. The default value of the `type` attribute is `normal`.

Blather provides a helper for each possible type:

Message#chat?
Message#error?
Message#groupchat?
Message#headline?
Message#normal?

Blather treats the ‘type` attribute like a normal ruby object attribute providing a getter and setter. The default `type` is `chat`.

msg = Message.new
msg.type              # => :chat
msg.chat?             # => true
msg.type = :normal
msg.normal?           # => true
msg.chat?             # => false

msg.type = :invalid   # => RuntimeError

## “Body” Element

The ‘body` element contains human-readable XML character data that specifies the textual contents of the message; this child element is normally included but is optional.

Blather provides an attribute-like syntax for Message ‘body` elements.

msg = Message.new '[email protected]', 'message body'
msg.body  # => 'message body'

msg.body = 'other message'
msg.body  # => 'other message'

## “Subject” Element

The ‘subject` element contains human-readable XML character data that specifies the topic of the message.

Blather provides an attribute-like syntax for Message ‘subject` elements.

msg = Message.new '[email protected]', 'message body'
msg.subject = 'message subject'
msg.subject  # => 'message subject'

## “Thread” Element

The primary use of the XMPP ‘thread` element is to uniquely identify a conversation thread or “chat session” between two entities instantiated by Message stanzas of type `chat`. However, the XMPP thread element can also be used to uniquely identify an analogous thread between two entities instantiated by Message stanzas of type `headline` or `normal`, or among multiple entities in the context of a multi-user chat room instantiated by Message stanzas of type `groupchat`. It MAY also be used for Message stanzas not related to a human conversation, such as a game session or an interaction between plugins. The `thread` element is not used to identify individual messages, only conversations or messagingg sessions. The inclusion of the `thread` element is optional.

The value of the ‘thread` element is not human-readable and MUST be treated as opaque by entities; no semantic meaning can be derived from it, and only exact comparisons can be made against it. The value of the `thread` element MUST be a universally unique identifier (UUID) as described in [UUID].

The ‘thread` element MAY possess a ’parent’ attribute that identifies another thread of which the current thread is an offshoot or child; the value of the ‘parent’ must conform to the syntax of the ‘thread` element itself.

Blather provides an attribute-like syntax for Message ‘thread` elements.

msg = Message.new
msg.thread = '12345'
msg.thread                                  # => '12345'

Parent threads can be set using a hash:

msg.thread = {'parent-id' => 'thread-id'}
msg.thread                                  # => 'thread-id'
msg.parent_thread                           # => 'parent-id'

Direct Known Subclasses

MUCUser, PubSub::Event

Defined Under Namespace

Classes: Delay, MUCUser

Constant Summary collapse

VALID_TYPES =
[:chat, :error, :groupchat, :headline, :normal].freeze
VALID_CHAT_STATES =
[:active, :composing, :gone, :inactive, :paused].freeze
CHAT_STATE_NS =
'http://jabber.org/protocol/chatstates'.freeze
HTML_NS =
'http://jabber.org/protocol/xhtml-im'.freeze
HTML_BODY_NS =
'http://www.w3.org/1999/xhtml'.freeze

Constants inherited from XMPPNode

XMPPNode::BASE_NAMES

Instance Attribute Summary

Attributes inherited from Blather::Stanza

#handler_hierarchy

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Blather::Stanza

#as_error, #from, #from=, handler_list, #id, #id=, #initialize, next_id, register, #reply, #reply!, #to, #to=, #type

Methods inherited from XMPPNode

class_from_registration, #decorate, decorator_modules, parse, register, #to_stanza

Constructor Details

This class inherits a constructor from Blather::Stanza

Class Method Details

.import(node) ⇒ Object



176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# File 'lib/blather/stanza/message.rb', line 176

def self.import(node)
  klass = nil
  node.children.detect do |e|
    ns = e.namespace ? e.namespace.href : nil
    klass = class_from_registration(e.element_name, ns)
  end

  if klass == Blather::Stanza::Presence::MUCUser
    klass = Blather::Stanza::Message::MUCUser
  end

  if klass && klass != self && ![Blather::Stanza::X, Blather::Stanza::Iq].include?(klass)
    klass.import(node)
  else
    new(node[:type]).inherit(node)
  end
end

.new(to = nil, body = nil, type = :chat) ⇒ Object

Create a new Message stanza

Parameters:

  • to (#to_s) (defaults to: nil)

    the JID to send the message to

  • body (#to_s) (defaults to: nil)

    the body of the message

  • type (Symbol) (defaults to: :chat)

    the message type. Must be one of VALID_TYPES



199
200
201
202
203
204
205
206
# File 'lib/blather/stanza/message.rb', line 199

def self.new(to = nil, body = nil, type = :chat)
  node = super :message
  node.to = to
  node.type = type
  node.body = body
  node.chat_state = :active if [:chat, :groupchat].include?(type)
  node
end

Instance Method Details

#bodyString

Get the message body

Returns:

  • (String)


264
265
266
# File 'lib/blather/stanza/message.rb', line 264

def body
  read_content :body
end

#body=(body) ⇒ Object

Set the message body

Parameters:

  • body (#to_s)

    the message body



271
272
273
# File 'lib/blather/stanza/message.rb', line 271

def body=(body)
  set_content_for :body, body
end

#chat?true, false

Check if the Message is of type :chat

Returns:

  • (true, false)


219
220
221
# File 'lib/blather/stanza/message.rb', line 219

def chat?
  self.type == :chat
end

#chat_stateSymbol

Get the message chat state

Returns:

  • (Symbol)


360
361
362
363
364
# File 'lib/blather/stanza/message.rb', line 360

def chat_state
  if (elem = find_first('ns:*', :ns => CHAT_STATE_NS)) && VALID_CHAT_STATES.include?(name = elem.name.to_sym)
    name
  end
end

#chat_state=(chat_state) ⇒ Object

Set the message chat state

Parameters:

  • chat_state (#to_s)

    the message chat state. Must be one of VALID_CHAT_STATES



369
370
371
372
373
374
375
376
377
378
379
380
381
# File 'lib/blather/stanza/message.rb', line 369

def chat_state=(chat_state)
  if chat_state && !VALID_CHAT_STATES.include?(chat_state.to_sym)
    raise ArgumentError, "Invalid Chat State (#{chat_state}), use: #{VALID_CHAT_STATES*' '}"
  end

  xpath('ns:*', :ns => CHAT_STATE_NS).remove

  if chat_state
    state = XMPPNode.new(chat_state, self.document)
    state.namespace = CHAT_STATE_NS
    self << state
  end
end

#delayObject



383
384
385
386
387
# File 'lib/blather/stanza/message.rb', line 383

def delay
  if d = find_first('ns:delay', :ns => "urn:xmpp:delay")
    Delay.new d
  end
end

#delayed?Boolean

Returns:

  • (Boolean)


389
390
391
# File 'lib/blather/stanza/message.rb', line 389

def delayed?
  !!delay
end

#error?true, false

Check if the Message is of type :error

Returns:

  • (true, false)


226
227
228
# File 'lib/blather/stanza/message.rb', line 226

def error?
  self.type == :error
end

#formObject

Returns the message’s x:data form child



353
354
355
# File 'lib/blather/stanza/message.rb', line 353

def form
  X.find_or_create self
end

#groupchat?true, false

Check if the Message is of type :groupchat

Returns:

  • (true, false)


233
234
235
# File 'lib/blather/stanza/message.rb', line 233

def groupchat?
  self.type == :groupchat
end

#headline?true, false

Check if the Message is of type :headline

Returns:

  • (true, false)


240
241
242
# File 'lib/blather/stanza/message.rb', line 240

def headline?
  self.type == :headline
end

#inherit(node) ⇒ Object

Overrides the parent method to ensure the current chat state is removed

See Also:

  • Iq#inherit


211
212
213
214
# File 'lib/blather/stanza/message.rb', line 211

def inherit(node)
  xpath('ns:*', :ns => CHAT_STATE_NS).remove
  super
end

#normal?true, false

Check if the Message is of type :normal

Returns:

  • (true, false)


247
248
249
# File 'lib/blather/stanza/message.rb', line 247

def normal?
  self.type == :normal
end

#parent_threadString?

Get the parent thread

Returns:

  • (String, nil)


333
334
335
336
# File 'lib/blather/stanza/message.rb', line 333

def parent_thread
  n = find_first('thread')
  n[:parent] if n
end

#subjectString

Get the message subject

Returns:

  • (String)


312
313
314
# File 'lib/blather/stanza/message.rb', line 312

def subject
  read_content :subject
end

#subject=(subject) ⇒ Object

Set the message subject

Parameters:

  • body (#to_s)

    the message subject



319
320
321
# File 'lib/blather/stanza/message.rb', line 319

def subject=(subject)
  set_content_for :subject, subject
end

#threadString

Get the message thread

Returns:

  • (String)


326
327
328
# File 'lib/blather/stanza/message.rb', line 326

def thread
  read_content :thread
end

#thread=(hash) ⇒ Object #thread=(thread) ⇒ Object

Set the thread

Overloads:

  • #thread=(hash) ⇒ Object

    Set a thread with a parent

    Parameters:

  • #thread=(thread) ⇒ Object

    Set a thread id

    Parameters:

    • thread (#to_s)

      the new thread id



346
347
348
349
350
# File 'lib/blather/stanza/message.rb', line 346

def thread=(thread)
  parent, thread = thread.to_a.flatten if thread.is_a?(Hash)
  set_content_for :thread, thread
  find_first('thread')[:parent] = parent
end

#type=(type) ⇒ Object

Ensures type is :get, :set, :result or :error

Parameters:

  • type (#to_sym)

    the Message type. Must be one of VALID_TYPES



254
255
256
257
258
259
# File 'lib/blather/stanza/message.rb', line 254

def type=(type)
  if type && !VALID_TYPES.include?(type.to_sym)
    raise ArgumentError, "Invalid Type (#{type}), use: #{VALID_TYPES*' '}"
  end
  super
end

#xhtmlString

Get the message xhtml

Returns:

  • (String)


297
298
299
# File 'lib/blather/stanza/message.rb', line 297

def xhtml
  self.xhtml_node.inner_html.strip
end

#xhtml=(xhtml_body) ⇒ Object

Set the message xhtml This will use Nokogiri to ensure the xhtml is valid

Parameters:

  • valid (#to_s)

    xhtml



305
306
307
# File 'lib/blather/stanza/message.rb', line 305

def xhtml=(xhtml_body)
  self.xhtml_node.inner_html = Nokogiri::XML::DocumentFragment.parse(xhtml_body)
end

#xhtml_nodeXML::Node

Get the message xhtml node This will create the node if it doesn’t exist

Returns:

  • (XML::Node)


279
280
281
282
283
284
285
286
287
288
289
290
291
292
# File 'lib/blather/stanza/message.rb', line 279

def xhtml_node
  unless h = find_first('ns:html', :ns => HTML_NS) || find_first('ns:html', :ns => HTML_BODY_NS)
    self << (h = XMPPNode.new('html', self.document))
    h.namespace = HTML_NS
  end

  unless b = h.find_first('ns:body', :ns => HTML_BODY_NS)
    b = XMPPNode.new('body', self.document)
    b.namespace = HTML_BODY_NS
    h << b
  end

  b
end