Class Jabber::MUC::MUCClient
In: lib/xmpp4r/muc/helper/mucclient.rb
Parent: Object
Message Presence XMPPStanza Iq XMPPElement ErrorResponse X IqQuery JabberError ComponentAuthenticationFailure ArgumentError InvalidChatState SOCKS5Error ServerError NoNameXmlnsRegistered ServerDisconnected ClientAuthenticationFailure Connection Client Component Connection Client Singleton IdGenerator Comparable JID Enumerable CallbackList Items Publish StandardError REXML::Element Stream XMPPElement Location IqFeature StreamHost IqSiFile IqSiFileRange IqSi StreamHostUsed XRosterItem RosterItem C Body HTML UserItem XMUCUserInvite Configuration Retract IqPubSub Item IqPubSubOwner Event Subscription Unsubscribe Tune XDataField XDataReported XDataTitle XDataInstructions Feature Item Identity IqVcard PubSub::ServiceHelper Helper Helper SOCKS5Bytestreams SOCKS5BytestreamsTarget SOCKS5BytestreamsInitiator SOCKS5BytestreamsServerStreamHost TCPSocket SOCKS5Socket IqQuery IqQueryBytestreams IqQueryRoster IqQueryVersion IqQueryRPC IqQueryMUCOwner IqQueryMUCAdmin IqQueryDiscoItems IqQueryDiscoInfo IqQueryLastActivity IBB IBBTarget IBBInitiator RosterXItem XRoster RosterX X XDelay XMUC XMUCUser XData Responder SimpleResponder Iq IqCommand XMLRPC::ParserWriterChooseMixin Client Server XMLRPC::ParseContentType XMLRPC::BasicServer MUCClient SimpleMUCClient MUC::UserItem XMUCUserItem IqQueryMUCAdminItem XParent SubscriptionConfig NodeConfig OwnerNodeConfig EventItems ServiceHelper OAuthServiceHelper NodeHelper EventItem Base Anonymous DigestMD5 Plain FileSource Base Bot Callback StreamParser Semaphore SOCKS5BytestreamsPeer SOCKS5BytestreamsServer IBBQueueItem Helper Responder Helper Listener MUCBrowser NodeBrowser ListenerMocker Helper Responder Helper Helper 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 XParent lib/xmpp4r/location/helper/helper.rb lib/xmpp4r/location/location.rb UserLocation lib/xmpp4r/feature_negotiation/iq/feature.rb FeatureNegotiation 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/roster/x/roster.rb lib/xmpp4r/roster/helper/roster.rb lib/xmpp4r/roster/iq/roster.rb Roster lib/xmpp4r/version/helper/responder.rb lib/xmpp4r/version/helper/simpleresponder.rb lib/xmpp4r/version/iq/version.rb Version lib/xmpp4r/command/iq/command.rb lib/xmpp4r/command/helper/responder.rb Command lib/xmpp4r/caps/helper/helper.rb lib/xmpp4r/caps/c.rb Caps lib/xmpp4r/reliable.rb Reliable lib/xmpp4r/delay/x/delay.rb Delay lib/xmpp4r/xhtml/html.rb XHTML lib/xmpp4r/rpc/helper/server.rb lib/xmpp4r/rpc/helper/client.rb lib/xmpp4r/rpc/iq/rpc.rb RPC 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/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/helper/oauth_service_helper.rb lib/xmpp4r/pubsub/children/event.rb lib/xmpp4r/pubsub/iq/pubsub.rb lib/xmpp4r/pubsub/children/retract.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 OAuthPubSubStreamHelper PubSub lib/xmpp4r/httpbinding/client.rb HTTPBinding lib/xmpp4r/tune/helper/helper.rb lib/xmpp4r/tune/tune.rb UserTune lib/xmpp4r/sasl.rb SASL lib/xmpp4r/test/listener_mocker.rb Test lib/xmpp4r/dataforms/x/data.rb Dataforms lib/xmpp4r/discovery/helper/helper.rb lib/xmpp4r/discovery/iq/discoinfo.rb lib/xmpp4r/discovery/helper/responder.rb lib/xmpp4r/discovery/iq/discoitems.rb Discovery lib/xmpp4r/bytestreams/helper/filetransfer.rb TransferSource FileTransfer lib/xmpp4r/last/helper/helper.rb lib/xmpp4r/last/iq/last.rb LastActivity 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_110_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]