Class Jabber::MUC::MUCClient
In: lib/xmpp4r/muc/helper/mucclient.rb
Parent: Object
XMPPElement ErrorResponse XMPPStanza X IqQuery Message Presence Iq JabberError ComponentAuthenticationFailure ArgumentError SOCKS5Error ServerError NoNameXmlnsRegistered ClientAuthenticationFailure Connection Client Component Client Singleton IdGenerator Comparable JID StandardError REXML::Element Stream IqQuery IqQueryRoster IqQueryBytestreams IqQueryVersion IqQueryRPC IqQueryMUCOwner IqQueryMUCAdmin IqQueryDiscoItems IqQueryDiscoInfo XRosterItem RosterXItem XMPPElement RosterItem StreamHost IqSiFile IqSiFileRange IqSi StreamHostUsed IqFeature C Body HTML UserItem XMUCUserInvite Configuration Items Item IqPubSub Publish Event IqPubSubOwner Subscription Unsubscribe Tune XDataField XDataReported XDataTitle XDataInstructions Feature Item Identity IqVcard XRoster RosterX X XMUC XMUCUser XDelay XData Iq IqCommand SOCKS5Bytestreams SOCKS5BytestreamsTarget SOCKS5BytestreamsInitiator SOCKS5BytestreamsServerStreamHost TCPSocket SOCKS5Socket IBB IBBTarget IBBInitiator Responder SimpleResponder XMLRPC::ParserWriterChooseMixin Client Server XMLRPC::ParseContentType XMLRPC::BasicServer MUCClient SimpleMUCClient MUC::UserItem XMUCUserItem IqQueryMUCAdminItem XParent SubscriptionConfig NodeConfig OwnerNodeConfig EventItems EventItem ServiceHelper NodeHelper Base Anonymous DigestMD5 Plain PubSub::ServiceHelper Helper FileSource Base Bot CallbackList Callback StreamParser Semaphore Helper Responder SOCKS5BytestreamsPeer SOCKS5BytestreamsServer IBBQueueItem Helper MUCBrowser NodeBrowser Helper Responder Helper lib/xmpp4r/message.rb lib/xmpp4r/connection.rb lib/xmpp4r/xmppstanza.rb lib/xmpp4r/iq.rb lib/xmpp4r/callbacks.rb lib/xmpp4r/idgenerator.rb lib/xmpp4r/stream.rb lib/xmpp4r/client.rb lib/xmpp4r/jid.rb lib/xmpp4r/x.rb lib/xmpp4r/streamparser.rb lib/xmpp4r/semaphore.rb lib/xmpp4r/errors.rb lib/xmpp4r/component.rb lib/xmpp4r/presence.rb lib/xmpp4r/xmppelement.rb lib/xmpp4r/query.rb lib/xmpp4r/roster/x/roster.rb lib/xmpp4r/roster/helper/roster.rb lib/xmpp4r/roster/iq/roster.rb Roster lib/xmpp4r/command/iq/command.rb lib/xmpp4r/command/helper/responder.rb Command XParent lib/xmpp4r/bytestreams/iq/si.rb lib/xmpp4r/bytestreams/helper/ibb/initiator.rb lib/xmpp4r/bytestreams/helper/socks5bytestreams/base.rb lib/xmpp4r/bytestreams/iq/bytestreams.rb lib/xmpp4r/bytestreams/helper/socks5bytestreams/initiator.rb lib/xmpp4r/bytestreams/helper/socks5bytestreams/target.rb lib/xmpp4r/bytestreams/helper/socks5bytestreams/socks5.rb lib/xmpp4r/bytestreams/helper/ibb/target.rb lib/xmpp4r/bytestreams/helper/socks5bytestreams/server.rb lib/xmpp4r/bytestreams/helper/ibb/base.rb Bytestreams lib/xmpp4r/version/helper/responder.rb lib/xmpp4r/version/helper/simpleresponder.rb lib/xmpp4r/version/iq/version.rb Version lib/xmpp4r/feature_negotiation/iq/feature.rb FeatureNegotiation lib/xmpp4r/caps/helper/helper.rb lib/xmpp4r/caps/c.rb Caps lib/xmpp4r/rpc/helper/server.rb lib/xmpp4r/rpc/helper/client.rb lib/xmpp4r/rpc/iq/rpc.rb RPC lib/xmpp4r/xhtml/html.rb XHTML lib/xmpp4r/muc/iq/mucadminitem.rb lib/xmpp4r/muc/x/muc.rb lib/xmpp4r/muc/item.rb lib/xmpp4r/muc/helper/simplemucclient.rb lib/xmpp4r/muc/iq/mucadmin.rb lib/xmpp4r/muc/helper/mucbrowser.rb lib/xmpp4r/muc/x/mucuseritem.rb lib/xmpp4r/muc/x/mucuserinvite.rb lib/xmpp4r/muc/iq/mucowner.rb lib/xmpp4r/muc/helper/mucclient.rb MUC lib/xmpp4r/delay/x/delay.rb Delay lib/xmpp4r/pubsub/children/item.rb lib/xmpp4r/pubsub/children/configuration.rb lib/xmpp4r/pubsub/children/subscription.rb lib/xmpp4r/pubsub/helper/servicehelper.rb lib/xmpp4r/pubsub/children/unsubscribe.rb lib/xmpp4r/pubsub/children/publish.rb lib/xmpp4r/pubsub/children/event.rb lib/xmpp4r/pubsub/iq/pubsub.rb lib/xmpp4r/pubsub/helper/nodebrowser.rb lib/xmpp4r/pubsub/helper/nodehelper.rb lib/xmpp4r/pubsub/children/items.rb lib/xmpp4r/pubsub/children/subscription_config.rb lib/xmpp4r/pubsub/children/node_config.rb PubSub lib/xmpp4r/sasl.rb SASL lib/xmpp4r/httpbinding/client.rb HTTPBinding lib/xmpp4r/tune/helper/helper.rb lib/xmpp4r/tune/tune.rb UserTune lib/xmpp4r/dataforms/x/data.rb Dataforms lib/xmpp4r/bytestreams/helper/filetransfer.rb TransferSource FileTransfer lib/xmpp4r/discovery/iq/discoinfo.rb lib/xmpp4r/discovery/helper/responder.rb lib/xmpp4r/discovery/iq/discoitems.rb Discovery lib/xmpp4r/framework/base.rb lib/xmpp4r/framework/bot.rb Framework lib/xmpp4r/vcard/helper/vcard.rb lib/xmpp4r/vcard/iq/vcard.rb Vcard Jabber dot/m_99_0.png

The MUCClient Helper handles low-level stuff of the Multi-User Chat (JEP 0045).

Use one instance per room.

Note that one client cannot join a single room multiple times. At least the clients’ resources must be different. This is a protocol design issue. But don‘t consider it as a bug, it is just a clone-preventing feature.

Methods

Attributes

jid  [R]  MUC JID
jid:[JID] room@component/nick
my_jid  [RW]  Sender JID, set this to use MUCClient from Components
my_jid:[JID] Defaults to nil
roster  [R]  MUC room roster
roster:[Hash] of [String] Nick => [Presence]

Public Class methods

Initialize a MUCClient

Call MUCClient#join after you have registered your callbacks to avoid reception of stanzas after joining and before registration of callbacks.

stream:[Stream] to operate on

[Source]

    # File lib/xmpp4r/muc/helper/mucclient.rb, line 45
45:       def initialize(stream)
46:         # Attributes initialization
47:         @stream = stream
48:         @my_jid = nil
49:         @jid = nil
50:         @roster = {}
51:         @roster_lock = Mutex.new
52: 
53:         @active = false
54: 
55:         @join_cbs = CallbackList.new
56:         @leave_cbs = CallbackList.new
57:         @presence_cbs = CallbackList.new
58:         @message_cbs = CallbackList.new
59:         @private_message_cbs = CallbackList.new
60:       end

Public Instance methods

Is the MUC client active?

This is false after initialization, true after joining and false after exit/kick

[Source]

     # File lib/xmpp4r/muc/helper/mucclient.rb, line 161
161:       def active?
162:         @active
163:       end

Add a callback for <presence/> stanzas indicating availability of a MUC participant

This callback will not be called for initial presences when a client joins a room, but only for the presences afterwards.

The callback will be called from MUCClient#handle_presence with one argument: the <presence/> stanza. Note that this stanza will have been already inserted into MUCClient#roster.

[Source]

     # File lib/xmpp4r/muc/helper/mucclient.rb, line 268
268:       def add_join_callback(prio = 0, ref = nil, &block)
269:         @join_cbs.add(prio, ref, block)
270:       end

Add a callback for <presence/> stanzas indicating unavailability of a MUC participant

The callback will be called with one argument: the <presence/> stanza.

Note that this is called just before the stanza is removed from MUCClient#roster, so it is still possible to see the last presence in the given block.

If the presence‘s origin is your MUC JID, the MUCClient will be deactivated afterwards.

[Source]

     # File lib/xmpp4r/muc/helper/mucclient.rb, line 284
284:       def add_leave_callback(prio = 0, ref = nil, &block)
285:         @leave_cbs.add(prio, ref, block)
286:       end

Add a callback for <message/> stanza directed to the whole room.

See MUCClient#add_private_message_callback for private messages between MUC participants.

[Source]

     # File lib/xmpp4r/muc/helper/mucclient.rb, line 301
301:       def add_message_callback(prio = 0, ref = nil, &block)
302:         @message_cbs.add(prio, ref, block)
303:       end

Add a callback for a <presence/> stanza which is neither a join nor a leave. This will be called when a room participant simply changes his status.

[Source]

     # File lib/xmpp4r/muc/helper/mucclient.rb, line 292
292:       def add_presence_callback(prio = 0, ref = nil, &block)
293:         @presence_cbs.add(prio, ref, block)
294:       end

Add a callback for <message/> stanza with type=‘chat’.

These stanza are normally not broadcasted to all room occupants but are some sort of private messaging.

[Source]

     # File lib/xmpp4r/muc/helper/mucclient.rb, line 310
310:       def add_private_message_callback(prio = 0, ref = nil, &block)
311:         @private_message_cbs.add(prio, ref, block)
312:       end

Use this method to configure a MUC room of which you are the owner.

options:[Hash] where keys are the features of the room you wish

to configure. See www.xmpp.org/extensions/xep-0045.html#registrar-formtype-owner

[Source]

     # File lib/xmpp4r/muc/helper/mucclient.rb, line 399
399:       def configure(options={})
400:         get_room_configuration
401:         submit_room_configuration(options)
402:       end

Exit the room

  • Sends presence with type=‘unavailable’ with an optional reason in <status/>,
  • then waits for a reply from the MUC component (will be processed by leave-callbacks),
  • then deletes callbacks from the stream.
reason:[String] Optional custom exit message

[Source]

     # File lib/xmpp4r/muc/helper/mucclient.rb, line 130
130:       def exit(reason=nil)
131:         unless active?
132:           raise "MUCClient hasn't yet joined"
133:         end
134: 
135:         pres = Presence.new
136:         pres.type = :unavailable
137:         pres.to = jid
138:         pres.from = @my_jid
139:         pres.status = reason if reason
140:         @stream.send(pres) { |r|
141:           Jabber::debuglog "exit: #{r.to_s.inspect}"
142:           if r.kind_of?(Presence) and r.type == :unavailable and r.from == jid
143:             @leave_cbs.process(r)
144:             true
145:           else
146:             false
147:           end
148:         }
149: 
150:         deactivate
151: 
152:         self
153:       end

Does this JID belong to that room?

jid:[JID]
result:[true] or [false]

[Source]

     # File lib/xmpp4r/muc/helper/mucclient.rb, line 318
318:       def from_room?(jid)
319:         @jid.strip == jid.strip
320:       end

[Source]

     # File lib/xmpp4r/muc/helper/mucclient.rb, line 404
404:       def get_room_configuration
405:         raise 'You are not the owner' unless owner?
406: 
407:         iq = Iq.new(:get, jid.strip)
408:         iq.from = my_jid
409:         iq.add(IqQueryMUCOwner.new)
410: 
411:         fields = []
412: 
413:         @stream.send_with_id(iq) do |answer|
414:           raise "Configuration not possible for this room" unless answer.query && answer.query.x(Dataforms::XData)
415: 
416:           answer.query.x(Dataforms::XData).fields.each do |field|
417:             if (var = field.attributes['var'])
418:               fields << var
419:             end
420:           end
421:         end
422: 
423:         fields
424:       end

Join a room

This registers its own callbacks on the stream provided to initialize and sends initial presence to the room. May throw ServerError if joining fails.

jid:[JID] room@component/nick
password:[String] Optional password
return:[MUCClient] self (chain-able)

[Source]

     # File lib/xmpp4r/muc/helper/mucclient.rb, line 72
 72:       def join(jid, password=nil)
 73:         if active?
 74:           raise "MUCClient already active"
 75:         end
 76: 
 77:         @jid = (jid.kind_of?(JID) ? jid : JID.new(jid))
 78:         activate
 79: 
 80:         # Joining
 81:         pres = Presence.new
 82:         pres.to = @jid
 83:         pres.from = @my_jid
 84:         xmuc = XMUC.new
 85:         xmuc.password = password
 86:         pres.add(xmuc)
 87: 
 88:         # We don't use Stream#send_with_id here as it's unknown
 89:         # if the MUC component *always* uses our stanza id.
 90:         error = nil
 91:         @stream.send(pres) { |r|
 92:           if from_room?(r.from) and r.kind_of?(Presence) and r.type == :error
 93:             # Error from room
 94:             error = r.error
 95:             true
 96:           # type='unavailable' may occur when the MUC kills our previous instance,
 97:           # but all join-failures should be type='error'
 98:           elsif r.from == jid and r.kind_of?(Presence) and r.type != :unavailable
 99:             # Our own presence reflected back - success
100:             if r.x(XMUCUser) and (i = r.x(XMUCUser).items.first)
101:               @affiliation = i.affiliation  # we're interested in if it's :owner
102:               @role = i.role                # :moderator ?
103:             end
104: 
105:             handle_presence(r, false)
106:             true
107:           else
108:             # Everything else
109:             false
110:           end
111:         }
112: 
113:         if error
114:           deactivate
115:           raise ServerError.new(error)
116:         end
117: 
118:         self
119:       end

The MUCClient‘s own nick (= resource)

result:[String] Nickname

[Source]

     # File lib/xmpp4r/muc/helper/mucclient.rb, line 169
169:       def nick
170:         @jid ? @jid.resource : nil
171:       end

Change nick

Threading is, again, suggested. This method waits for two <presence/> stanzas, one indicating unavailabilty of the old transient JID, one indicating availability of the new transient JID.

If the service denies nick-change, ServerError will be raised.

[Source]

     # File lib/xmpp4r/muc/helper/mucclient.rb, line 182
182:       def nick=(new_nick)
183:         unless active?
184:           raise "MUCClient not active"
185:         end
186: 
187:         new_jid = JID.new(@jid.node, @jid.domain, new_nick)
188: 
189:         # Joining
190:         pres = Presence.new
191:         pres.to = new_jid
192:         pres.from = @my_jid
193: 
194:         error = nil
195:         # Keeping track of the two stanzas enables us to process stanzas
196:         # which don't arrive in the order specified by JEP-0045
197:         presence_unavailable = false
198:         presence_available = false
199:         # We don't use Stream#send_with_id here as it's unknown
200:         # if the MUC component *always* uses our stanza id.
201:         @stream.send(pres) { |r|
202:           if from_room?(r.from) and r.kind_of?(Presence) and r.type == :error
203:             # Error from room
204:             error = r.error
205:           elsif r.from == @jid and r.kind_of?(Presence) and r.type == :unavailable and
206:                 r.x and r.x.kind_of?(XMUCUser) and r.x.status_code == 303
207:             # Old JID is offline, but wait for the new JID and let stanza be handled
208:             # by the standard callback
209:             presence_unavailable = true
210:             handle_presence(r)
211:           elsif r.from == new_jid and r.kind_of?(Presence) and r.type != :unavailable
212:             # Our own presence reflected back - success
213:             presence_available = true
214:             handle_presence(r)
215:           end
216: 
217:           if error or (presence_available and presence_unavailable)
218:             true
219:           else
220:             false
221:           end
222:         }
223: 
224:         if error
225:           raise ServerError.new(error)
226:         end
227: 
228:         # Apply new JID
229:         @jid = new_jid
230:       end

[Source]

     # File lib/xmpp4r/muc/helper/mucclient.rb, line 390
390:       def owner?
391:         @affiliation == :owner
392:       end

The room name (= node)

result:[String] Room name

[Source]

     # File lib/xmpp4r/muc/helper/mucclient.rb, line 236
236:       def room
237:         @jid ? @jid.node : nil
238:       end

Send a stanza to the room

If stanza is a Jabber::Message, stanza.type will be automatically set to :groupchat if directed to room or :chat if directed to participant.

stanza:[XMPPStanza] to send
to:[String] Stanza destination recipient, or room if nil

[Source]

     # File lib/xmpp4r/muc/helper/mucclient.rb, line 248
248:       def send(stanza, to=nil)
249:         if stanza.kind_of? Message
250:           stanza.type = to ? :chat : :groupchat
251:         end
252:         stanza.from = @my_jid
253:         stanza.to = JID.new(jid.node, jid.domain, to)
254:         @stream.send(stanza)
255:       end

Push a list of new affiliations to the room

items:[Array] of, or single [IqQueryMUCAdminItem]

[Source]

     # File lib/xmpp4r/muc/helper/mucclient.rb, line 448
448:       def send_affiliations(items)
449:         iq = Iq.new(:set, jid.strip)
450:         iq.from = my_jid
451:         iq.add(IqQueryMUCAdmin.new)
452: 
453:         items = [item] unless items.kind_of? Array
454:         items.each { |item|
455:           iq.query.add(item)
456:         }
457: 
458:         @stream.send_with_id(iq)
459:       end

[Source]

     # File lib/xmpp4r/muc/helper/mucclient.rb, line 426
426:       def submit_room_configuration(options)
427:         # fill out the reply form
428:         iq = Iq.new(:set, jid.strip)
429:         iq.from = my_jid
430:         query = IqQueryMUCOwner.new
431:         form = Dataforms::XData.new
432:         form.type = :submit
433:         options.each do |var, values|
434:           field = Dataforms::XDataField.new
435:           values = [values] unless values.is_a?(Array)
436:           field.var, field.values = var, values
437:           form.add(field)
438:         end
439:         query.add(form)
440:         iq.add(query)
441: 
442:         @stream.send_with_id(iq)
443:       end

[Validate]