# =XMPP4R - XMPP Library for Ruby # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option. # Website::http://home.gna.org/xmpp4r/ require 'xmpp4r/delay/x/delay' require 'xmpp4r/muc/helper/mucclient' require 'xmpp4r/muc/iq/mucadminitem' module Jabber module MUC ## # This class attempts to implement a lot of complexity of the # Multi-User Chat protocol. If you want to implement JEP0045 # yourself, use Jabber::MUC::MUCClient for some minor # abstraction. # # Minor flexibility penalty: the on_* callbacks are no # CallbackLists and may therefore only used once. A second # invocation will overwrite the previous set up block. # # *Hint:* the parameter time may be nil if the server didn't # send it. # # Example usage: # my_muc = Jabber::MUC::SimpleMUCClient.new(my_client) # my_muc.on_message { |time,nick,text| # puts (time || Time.new).strftime('%I:%M') + " <#{nick}> #{text}" # } # my_muc.join(Jabber::JID.new('jdev@conference.jabber.org/XMPP4R-Bot')) # # Please take a look at Jabber::MUC::MUCClient for # derived methods, such as MUCClient#join, MUCClient#exit, # ... class SimpleMUCClient < MUCClient ## # Initialize a SimpleMUCClient # stream:: [Stream] to operate on # jid:: [JID] room@component/nick # password:: [String] Optional password def initialize(stream) super @room_message_block = nil @message_block = nil @private_message_block = nil @subject_block = nil @subject = nil @join_block = nil add_join_callback(999) { |pres| # Presence time time = nil pres.each_element('x') { |x| if x.kind_of?(Delay::XDelay) time = x.stamp end } # Invoke... @join_block.call(time, pres.from.resource) if @join_block false } @leave_block = nil @self_leave_block = nil add_leave_callback(999) { |pres| # Presence time time = nil pres.each_element('x') { |x| if x.kind_of?(Delay::XDelay) time = x.stamp end } # Invoke... if pres.from == jid @self_leave_block.call(time) if @self_leave_block else @leave_block.call(time, pres.from.resource) if @leave_block end false } end private def handle_message(msg) super # Message time (e.g. history) time = nil msg.each_element('x') { |x| if x.kind_of?(Delay::XDelay) time = x.stamp end } sender_nick = msg.from.resource if msg.subject @subject = msg.subject @subject_block.call(time, sender_nick, @subject) if @subject_block end if msg.body if sender_nick.nil? @room_message_block.call(time, msg.body) if @room_message_block else if msg.type == :chat @private_message_block.call(time, msg.from.resource, msg.body) if @private_message_block elsif msg.type == :groupchat @message_block.call(time, msg.from.resource, msg.body) if @message_block else # ...? end end end end public ## # Room subject/topic # result:: [String] The subject def subject @subject end ## # Change the room's subject/topic # # This will not be reflected by SimpleMUCClient#subject # immediately, wait for SimpleMUCClient#on_subject # s:: [String] New subject def subject=(s) msg = Message.new msg.subject = s send(msg) end ## # Send a simple text message # text:: [String] Message body # to:: [String] Optional nick if directed to specific user def say(text, to=nil) send(Message.new(nil, text), to) end ## # Request the MUC to invite users to this room # # Sample usage: # my_muc.invite( {'wiccarocks@shakespeare.lit/laptop' => 'This coven needs both wiccarocks and hag66.', # 'hag66@shakespeare.lit' => 'This coven needs both hag66 and wiccarocks.'} ) # recipients:: [Hash] of [JID] => [String] Reason def invite(recipients) msg = Message.new x = msg.add(XMUCUser.new) recipients.each { |jid,reason| x.add(XMUCUserInvite.new(jid, reason)) } send(msg) end ## # Administratively remove one or more users from the room. # # Will wait for response, possibly raising ServerError # # Sample usage: # my_muc.kick 'pistol', 'Avaunt, you cullion!' # my_muc.kick(['Bill', 'Linus'], 'Stop flaming') # # recipients:: [Array] of, or single [String]: Nicks # reason:: [String] Kick reason def kick(recipients, reason) recipients = [recipients] unless recipients.kind_of? Array items = recipients.collect { |recipient| item = IqQueryMUCAdminItem.new item.nick = recipient item.role = :none item.reason = reason item } send_affiliations(items) end ## # Block to be invoked when a message *from* the room arrives # # Example: # Astro has joined this session # block:: Takes two arguments: time, text def on_room_message(&block) @room_message_block = block end ## # Block to be invoked when a message from a participant to # the whole room arrives # block:: Takes three arguments: time, sender nickname, text def on_message(&block) @message_block = block end ## # Block to be invoked when a private message from a participant # to you arrives. # block:: Takes three arguments: time, sender nickname, text def on_private_message(&block) @private_message_block = block end ## # Block to be invoked when somebody sets a new room subject # block:: Takes three arguments: time, nickname, new subject def on_subject(&block) @subject_block = block end ## # Block to be called when somebody enters the room # # If there is a non-nil time passed to the block, chances # are great that this is initial presence from a participant # after you have joined the room. # block:: Takes two arguments: time, nickname def on_join(&block) @join_block = block end ## # Block to be called when somebody leaves the room # block:: Takes two arguments: time, nickname def on_leave(&block) @leave_block = block end ## # Block to be called when *you* leave the room # # Deactivation occurs *afterwards*. # block:: Takes one argument: time def on_self_leave(&block) @self_leave_block = block end end end end