Class: Blather::Stanza::Message

Inherits:
Blather::Stanza show all
Defined in:
lib/blather/stanza/message.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

PubSub::Event

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

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Blather::Stanza

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

Methods inherited from XMPPNode

class_from_registration, #content_from, #inherit_attrs, #inspect, #namespace=, #namespace_href, #nokogiri_namespace=, #read_attr, #read_content, register, #remove_child, #remove_children, #set_content_for, #to_stanza, #write_attr

Methods inherited from Nokogiri::XML::Node

#[]=, #attr_set, #find_first, #nokogiri_xpath, #xpath

Class Method Details

.import(node) ⇒ Object



171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/blather/stanza/message.rb', line 171

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 && klass != self && klass != Blather::Stanza::X
    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



190
191
192
193
194
195
196
197
# File 'lib/blather/stanza/message.rb', line 190

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)


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

def body
  read_content :body
end

#body=(body) ⇒ Object

Set the message body

Parameters:

  • body (#to_s)

    the message body



262
263
264
# File 'lib/blather/stanza/message.rb', line 262

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

#chat?true, false

Check if the Message is of type :chat

Returns:

  • (true, false)


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

def chat?
  self.type == :chat
end

#chat_stateSymbol

Get the message chat state

Returns:

  • (Symbol)


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

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



360
361
362
363
364
365
366
367
368
369
370
371
372
# File 'lib/blather/stanza/message.rb', line 360

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

#error?true, false

Check if the Message is of type :error

Returns:

  • (true, false)


217
218
219
# File 'lib/blather/stanza/message.rb', line 217

def error?
  self.type == :error
end

#formObject

Returns the message’s x:data form child



344
345
346
# File 'lib/blather/stanza/message.rb', line 344

def form
  X.find_or_create self
end

#groupchat?true, false

Check if the Message is of type :groupchat

Returns:

  • (true, false)


224
225
226
# File 'lib/blather/stanza/message.rb', line 224

def groupchat?
  self.type == :groupchat
end

#headline?true, false

Check if the Message is of type :headline

Returns:

  • (true, false)


231
232
233
# File 'lib/blather/stanza/message.rb', line 231

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


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

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)


238
239
240
# File 'lib/blather/stanza/message.rb', line 238

def normal?
  self.type == :normal
end

#parent_threadString?

Get the parent thread

Returns:

  • (String, nil)


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

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

#subjectString

Get the message subject

Returns:

  • (String)


303
304
305
# File 'lib/blather/stanza/message.rb', line 303

def subject
  read_content :subject
end

#subject=(subject) ⇒ Object

Set the message subject

Parameters:

  • body (#to_s)

    the message subject



310
311
312
# File 'lib/blather/stanza/message.rb', line 310

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

#threadString

Get the message thread

Returns:

  • (String)


317
318
319
# File 'lib/blather/stanza/message.rb', line 317

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



337
338
339
340
341
# File 'lib/blather/stanza/message.rb', line 337

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



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

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)


288
289
290
# File 'lib/blather/stanza/message.rb', line 288

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



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

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)


270
271
272
273
274
275
276
277
278
279
280
281
282
283
# File 'lib/blather/stanza/message.rb', line 270

def xhtml_node
  unless h = find_first('ns:html', :ns => HTML_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