Class Jabber::MUC::MUCClient
In: lib/xmpp4r/muc/helper/mucclient.rb
Parent: Object
Message Presence XMPPStanza Iq Singleton IdGenerator XMPPElement X IqQuery Error Connection Client Component Client Comparable JID RuntimeError AuthenticationFailure ErrorException NoNameXmlnsRegistered SOCKS5Error REXML::Element Stream SOCKS5Bytestreams SOCKS5BytestreamsTarget SOCKS5BytestreamsInitiator XMPPElement StreamHost IqSiFileRange IqSiFile StreamHostUsed IqSi XRosterItem RosterItem IqFeature XMUCUserItem XMUCUserInvite IqPubSub Items Item Event Feature Item Identity XDataField XDataReported XDataTitle XDataInstructions IqVcard SOCKS5BytestreamsServerStreamHost TCPSocket SOCKS5Socket IqQuery IqQueryBytestreams IqQueryVersion IqQueryRoster IqQueryMUCOwner IqQueryRPC IqQueryDiscoItems IqQueryDiscoInfo IBB IBBTarget IBBInitiator Responder SimpleResponder Iq IqCommand RosterXItem XRoster RosterX X XMUCUser XMUC XDelay XData XParent MUCClient SimpleMUCClient XMLRPC::ParserWriterChooseMixin Client Server XMLRPC::ParseContentType XMLRPC::BasicServer Base DigestMD5 Plain ServiceHelper NodeHelper FileSource CallbackList Callback Semaphore StreamParser SOCKS5BytestreamsPeer SOCKS5BytestreamsServer IBBQueueItem Responder Helper MUCBrowser NodeBrowser Helper Helper lib/xmpp4r/authenticationfailure.rb lib/xmpp4r/xmppstanza.rb lib/xmpp4r/callbacks.rb lib/xmpp4r/idgenerator.rb lib/xmpp4r/connection.rb lib/xmpp4r/iq.rb lib/xmpp4r/jid.rb lib/xmpp4r/errorexception.rb lib/xmpp4r/client.rb lib/xmpp4r/stream.rb lib/xmpp4r/semaphore.rb lib/xmpp4r/streamparser.rb lib/xmpp4r/x.rb lib/xmpp4r/error.rb lib/xmpp4r/component.rb lib/xmpp4r/query.rb lib/xmpp4r/xmppelement.rb lib/xmpp4r/message.rb lib/xmpp4r/presence.rb lib/xmpp4r/bytestreams/helper/ibb/initiator.rb lib/xmpp4r/bytestreams/iq/si.rb lib/xmpp4r/bytestreams/iq/bytestreams.rb lib/xmpp4r/bytestreams/helper/socks5bytestreams/base.rb lib/xmpp4r/bytestreams/helper/socks5bytestreams/server.rb lib/xmpp4r/bytestreams/helper/socks5bytestreams/target.rb lib/xmpp4r/bytestreams/helper/socks5bytestreams/socks5.rb lib/xmpp4r/bytestreams/helper/socks5bytestreams/initiator.rb lib/xmpp4r/bytestreams/helper/ibb/base.rb lib/xmpp4r/bytestreams/helper/ibb/target.rb Bytestreams XParent lib/xmpp4r/version/iq/version.rb lib/xmpp4r/version/helper/responder.rb lib/xmpp4r/version/helper/simpleresponder.rb Version lib/xmpp4r/command/iq/command.rb lib/xmpp4r/command/helper/responder.rb Command lib/xmpp4r/roster/helper/roster.rb lib/xmpp4r/roster/iq/roster.rb lib/xmpp4r/roster/x/roster.rb Roster lib/xmpp4r/feature_negotiation/iq/feature.rb FeatureNegotiation lib/xmpp4r/muc/x/muc.rb lib/xmpp4r/muc/helper/mucclient.rb lib/xmpp4r/muc/x/mucuseritem.rb lib/xmpp4r/muc/helper/mucbrowser.rb lib/xmpp4r/muc/x/mucuserinvite.rb lib/xmpp4r/muc/iq/mucowner.rb lib/xmpp4r/muc/helper/simplemucclient.rb MUC lib/xmpp4r/rpc/helper/server.rb lib/xmpp4r/rpc/helper/client.rb lib/xmpp4r/rpc/iq/rpc.rb RPC lib/xmpp4r/sasl.rb SASL lib/xmpp4r/delay/x/delay.rb Delay lib/xmpp4r/pubsub/helper/servicehelper.rb lib/xmpp4r/pubsub/stanzas/item.rb lib/xmpp4r/pubsub/helper/nodehelper.rb lib/xmpp4r/pubsub/iq/pubsub.rb lib/xmpp4r/pubsub/stanzas/event.rb lib/xmpp4r/pubsub/helper/nodebrowser.rb lib/xmpp4r/pubsub/stanzas/items.rb PubSub lib/xmpp4r/httpbinding/client.rb HTTPBinding lib/xmpp4r/bytestreams/helper/filetransfer.rb TransferSource FileTransfer lib/xmpp4r/discovery/iq/discoinfo.rb lib/xmpp4r/discovery/iq/discoitems.rb Discovery lib/xmpp4r/dataforms/x/data.rb Dataforms lib/xmpp4r/vcard/helper/vcard.rb lib/xmpp4r/vcard/iq/vcard.rb Vcard Jabber dot/m_79_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 42
42:       def initialize(stream)
43:         # Attributes initialization
44:         @stream = stream
45:         @my_jid = nil
46:         @jid = nil
47:         @roster = {}
48:         @roster_lock = Mutex.new
49: 
50:         @active = false
51: 
52:         @join_cbs = CallbackList.new
53:         @leave_cbs = CallbackList.new
54:         @presence_cbs = CallbackList.new
55:         @message_cbs = CallbackList.new
56:         @private_message_cbs = CallbackList.new
57:       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 158
158:       def active?
159:         @active
160:       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 265
265:       def add_join_callback(prio = 0, ref = nil, &block)
266:         @join_cbs.add(prio, ref, block)
267:       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 281
281:       def add_leave_callback(prio = 0, ref = nil, &block)
282:         @leave_cbs.add(prio, ref, block)
283:       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 298
298:       def add_message_callback(prio = 0, ref = nil, &block)
299:         @message_cbs.add(prio, ref, block)
300:       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 289
289:       def add_presence_callback(prio = 0, ref = nil, &block)
290:         @presence_cbs.add(prio, ref, block)
291:       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 307
307:       def add_private_message_callback(prio = 0, ref = nil, &block)
308:         @private_message_cbs.add(prio, ref, block)
309:       end

[Source]

     # File lib/xmpp4r/muc/helper/mucclient.rb, line 391
391:       def configure(options={})
392:         raise 'You are not the owner' unless owner?
393:         
394:         iq = Iq.new(:get, jid)
395:         iq.to = @jid
396:         iq.from = @my_jid
397:         iq.add(IqQueryMUCOwner.new)
398: 
399:         fields = []
400:         
401:         answer = @stream.send_with_id(iq)
402:         raise "Configuration not possible for this room" unless answer.query && answer.query.x(XData)
403: 
404:         answer.query.x(XData).fields.each { |field|
405:           if (var = field.attributes['var'])
406:             fields << var
407:           end
408:         }
409: 
410:         
411:         # fill out the reply form
412:         iq = Iq.new(:set, jid)
413:         iq.to = @jid
414:         iq.from = @my_jid
415:         query = IqQueryMUCOwner.new
416:         form = Dataforms::XData.new
417:         form.type = :submit
418:         options.each do |var, values|
419:           field = Dataforms::XDataField.new
420:           values = [values] unless values.is_a?(Array)
421:           field.var, field.values = var, values
422:           form.add(field)
423:         end
424:         query.add(form)
425:         iq.add(query)   
426: 
427:         @stream.send_with_id(iq)
428:       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 127
127:       def exit(reason=nil)
128:         unless active?
129:           raise "MUCClient hasn't yet joined"
130:         end
131: 
132:         pres = Presence.new
133:         pres.type = :unavailable
134:         pres.to = jid
135:         pres.from = @my_jid
136:         pres.status = reason if reason
137:         @stream.send(pres) { |r|
138:           Jabber::debuglog "exit: #{r.to_s.inspect}"
139:           if r.kind_of?(Presence) and r.type == :unavailable and r.from == jid
140:             @leave_cbs.process(r)
141:             true
142:           else
143:             false
144:           end
145:         }
146: 
147:         deactivate
148: 
149:         self
150:       end

Does this JID belong to that room?

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

[Source]

     # File lib/xmpp4r/muc/helper/mucclient.rb, line 315
315:       def from_room?(jid)
316:         @jid.strip == jid.strip
317:       end

Join a room

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

The MUCClient‘s own nick (= resource)

result:[String] Nickname

[Source]

     # File lib/xmpp4r/muc/helper/mucclient.rb, line 166
166:       def nick
167:         @jid ? @jid.resource : nil
168:       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, ErrorException will be raisen.

[Source]

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

[Source]

     # File lib/xmpp4r/muc/helper/mucclient.rb, line 387
387:       def owner?
388:         @affiliation == :owner
389:       end

The room name (= node)

result:[String] Room name

[Source]

     # File lib/xmpp4r/muc/helper/mucclient.rb, line 233
233:       def room
234:         @jid ? @jid.node : nil
235:       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 245
245:       def send(stanza, to=nil)
246:         if stanza.kind_of? Message
247:           stanza.type = to ? :chat : :groupchat
248:         end
249:         stanza.from = @my_jid
250:         stanza.to = JID::new(jid.node, jid.domain, to)
251:         @stream.send(stanza)
252:       end

[Validate]