class Blather::Stanza::Message

# 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 'user@domain.tld/resource'
msg.to == 'user@domain.tld/resource'

msg.to = 'another-user@some-domain.tld/resource'
msg.to == 'another-user@some-domain.tld/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:

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 'user@domain.tld', '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 'user@domain.tld', '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'

@handler :message

Constants

CHAT_STATE_NS

@private

HTML_BODY_NS

@private

HTML_NS

@private

VALID_CHAT_STATES

@private

VALID_TYPES

@private

Public Class Methods

import(node) click to toggle source

@private

# 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) click to toggle source

Create a new Message stanza

@param [#to_s] to the JID to send the message to @param [#to_s] body the body of the message @param [Symbol] type the message type. Must be one of VALID_TYPES

Calls superclass method Blather::Stanza::new
# 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

Public Instance Methods

body() click to toggle source

Get the message body

@return [String]

# File lib/blather/stanza/message.rb, line 264
def body
  read_content :body
end
body=(body) click to toggle source

Set the message body

@param [#to_s] body the message body

# File lib/blather/stanza/message.rb, line 271
def body=(body)
  set_content_for :body, body
end
chat?() click to toggle source

Check if the Message is of type :chat

@return [true, false]

# File lib/blather/stanza/message.rb, line 219
def chat?
  self.type == :chat
end
chat_state() click to toggle source

Get the message chat state

@return [Symbol]

# 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) click to toggle source

Set the message chat state

@param [#to_s] chat_state the message chat state. Must be one of VALID_CHAT_STATES

# 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
delay() click to toggle source
# 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?() click to toggle source
# File lib/blather/stanza/message.rb, line 389
def delayed?
  !!delay
end
error?() click to toggle source

Check if the Message is of type :error

@return [true, false]

# File lib/blather/stanza/message.rb, line 226
def error?
  self.type == :error
end
form() click to toggle source

Returns the message's x:data form child

# File lib/blather/stanza/message.rb, line 353
def form
  X.find_or_create self
end
groupchat?() click to toggle source

Check if the Message is of type :groupchat

@return [true, false]

# File lib/blather/stanza/message.rb, line 233
def groupchat?
  self.type == :groupchat
end
headline?() click to toggle source

Check if the Message is of type :headline

@return [true, false]

# File lib/blather/stanza/message.rb, line 240
def headline?
  self.type == :headline
end
inherit(node) click to toggle source

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

@see Blather::Stanza::Iq#inherit

Calls superclass method
# File lib/blather/stanza/message.rb, line 211
def inherit(node)
  xpath('ns:*', :ns => CHAT_STATE_NS).remove
  super
end
normal?() click to toggle source

Check if the Message is of type :normal

@return [true, false]

# File lib/blather/stanza/message.rb, line 247
def normal?
  self.type == :normal
end
parent_thread() click to toggle source

Get the parent thread

@return [String, nil]

# File lib/blather/stanza/message.rb, line 333
def parent_thread
  n = find_first('thread')
  n[:parent] if n
end
subject() click to toggle source

Get the message subject

@return [String]

# File lib/blather/stanza/message.rb, line 312
def subject
  read_content :subject
end
subject=(subject) click to toggle source

Set the message subject

@param [#to_s] body the message subject

# File lib/blather/stanza/message.rb, line 319
def subject=(subject)
  set_content_for :subject, subject
end
thread() click to toggle source

Get the message thread

@return [String]

# File lib/blather/stanza/message.rb, line 326
def thread
  read_content :thread
end
thread=(thread) click to toggle source

Set the thread

@overload thread=(hash)

Set a thread with a parent
@param [Hash<parent-id => thread-id>] thread

@overload thread=(thread)

Set a thread id
@param [#to_s] thread the new thread id
# 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) click to toggle source

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

@param [#to_sym] type the Message type. Must be one of VALID_TYPES

Calls superclass method Blather::Stanza#type=
# 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
xhtml() click to toggle source

Get the message xhtml

@return [String]

# File lib/blather/stanza/message.rb, line 297
def xhtml
  self.xhtml_node.inner_html.strip
end
xhtml=(xhtml_body) click to toggle source

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

@param [#to_s] valid xhtml

# 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_node() click to toggle source

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

@return [XML::Node]

# 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