lib/marvin/abstract_client.rb in Sutto-marvin-0.4.0 vs lib/marvin/abstract_client.rb in Sutto-marvin-0.8.0.0

- old
+ new

@@ -1,260 +1,146 @@ -require 'ostruct' -require 'active_support' require "marvin/irc/event" module Marvin class AbstractClient - include Marvin::Dispatchable + is :dispatchable, :loggable - def initialize(opts = {}) - self.original_opts = opts.dup # Copy the options so we can use them to reconnect. - self.server = opts[:server] - self.port = opts[:port] - self.default_channels = opts[:channels] - self.nicks = opts[:nicks] || [] - self.pass = opts[:pass] + def initialize(opts) + opts = opts.to_nash if opts.is_a?(Hash) + @connection_config = opts.dup # Copy the options so we can use them to reconnect. + @server = opts.server + @port = opts.port + @default_channels = opts.channels + @nicks = opts.nicks || [] + @pass = opts.pass end - cattr_accessor :events, :configuration, :logger, :is_setup, :connections - attr_accessor :channels, :nickname, :server, :port, :nicks, :pass, :disconnect_expected, :original_opts + cattr_accessor :events, :configuration, :is_setup, :connections, :development + attr_accessor :channels, :nickname, :server, :port, :nicks, :pass, + :disconnect_expected, :connection_config # Set the default values for the variables - self.events = [] - self.configuration = OpenStruct.new - self.configuration.channels = [] - self.connections = [] - + @@events = [] + @@configuration = Marvin::Nash.new + @@connections = [] + @@development = false + # Initializes the instance variables used for the # current connection, dispatching a :client_connected event # once it has finished. During this process, it will # call #client= on each handler if they respond to it. def process_connect self.class.setup logger.info "Initializing the current instance" - self.channels = [] - self.connections << self + @channels = [] + connections << self logger.info "Setting the client for each handler" - self.handlers.each { |h| h.client = self if h.respond_to?(:client=) } + setup_handlers logger.info "Dispatching the default :client_connected event" dispatch :client_connected end def process_disconnect - logger.info "Handling disconnect for #{self.server}:#{self.port}" - self.connections.delete(self) if self.connections.include?(self) + logger.info "Handling disconnect for #{host_with_port}" + connections.delete(self) dispatch :client_disconnected - unless self.disconnect_expected - logger.warn "Lost connection to server - adding reconnect" - self.class.add_reconnect self.original_opts + unless @disconnect_expected + logger.warn "Unexpectly lost connection to server; adding reconnect" + self.class.add_reconnect @connection_config else - Marvin::Loader.stop! if self.connections.blank? + Marvin::Loader.stop! if connections.blank? end end + def setup_handlers + handlers.each { |h| h.client = self if h.respond_to?(:client=) } + end + + def process_development + if @@development + Marvin::Reloading.reload! + setup_handlers + end + end + + def dispatch(*args) + process_development + super + end + # Sets the current class-wide settings of this IRC Client # to either an OpenStruct or the results of #to_hash on # any other value that is passed in. def self.configuration=(config) - @@configuration = config.is_a?(OpenStruct) ? config : OpenStruct.new(config.to_hash) + config = Marvin::Nash.new(config.to_hash) unless config.is_a?(Marvin::Nash) + @@configuration = config.normalized end - # Initializes class-wide settings and those that - # are required such as the logger. by default, it - # will convert the channel option of the configuration - # to be channels - hence normalising it into a format - # that is more widely used throughout the client. + def self.setup? + @setup ||= false + end + def self.setup - return if self.is_setup - if configuration.logger.blank? - require 'logger' - configuration.logger = Marvin::Logger.logger + return if setup? + configure + end + + def self.configure + config = Marvin::Nash.new + config.merge! Marvin::Settings.configuration + if block_given? + yield(nash = Marvin::Nash.new) + config.merge! nash end - self.logger = self.configuration.logger - self.is_setup = true + @@configuration = config + # Help is only currently available on an instance running + # distributed handler. + Marvin::CoreCommands.register! unless Marvin::Distributed::Handler.registered? + @setup = true end ## Handling all of the the actual client stuff. def receive_line(line) dispatch :incoming_line, :line => line - event = Marvin::Settings.default_parser.parse(line) - dispatch(event.to_incoming_event_name, event.to_hash) unless event.nil? + event = Marvin::Settings.parser.parse(line) + dispatch(event.to_incoming_event_name, event.to_hash) unless event.nil? end - # Default handlers - - # The default handler for all things initialization-related - # on the client. Usually, this will send the user command, - # set out nick, join all of the channels / rooms we wish - # to be in and if a password is specified in the configuration, - # it will also attempt to identify us. - def handle_client_connected(opts = {}) - logger.info "About to handle client connected" - # If the pass is set - unless self.pass.blank? - logger.info "Sending pass for connection" - command :pass, self.pass - end - # IRC Connection is establish so we send all the required commands to the server. - logger.info "Setting default nickname" - default_nickname = self.nicks.shift - nick default_nickname - logger.info "sending user command" - command :user, self.configuration.user, "0", "*", Marvin::Util.last_param(self.configuration.name) - rescue Exception => e - Marvin::ExceptionTracker.log(e) - end - def default_channels @default_channels ||= [] end def default_channels=(channels) @default_channels = channels.to_a.map { |c| c.to_s } end def host_with_port - @host_with_port ||= "#{self.server}:#{self.port}" + @host_with_port ||= "#{server}:#{port}" end def nicks if @nicks.blank? && !@nicks_loaded logger.info "Setting default nick list" @nicks = [] - @nicks << self.configuration.nick - @nicks += self.configuration.nicks.to_a unless self.configuration.nicks.blank? + @nicks << configuration.nick if configuration.nick? + @nicks += configuration.nicks.to_a if configuration.nicks? @nicks.compact! + raise "No initial nicks for #{host_with_port}" if @nicks.blank? @nicks_loaded = true end return @nicks end - # The default response for PING's - it simply replies - # with a PONG. - def handle_incoming_ping(opts = {}) - logger.info "Received Incoming Ping - Handling with a PONG" - pong(opts[:data]) - end + # Break it down into a couple of different files. + require 'marvin/client/default_handlers' + require 'marvin/client/actions' - # TODO: Get the correct mapping for a given - # Code. - def handle_incoming_numeric(opts = {}) - case opts[:code] - when Marvin::IRC::Replies[:RPL_WELCOME] - handle_welcome - when Marvin::IRC::Replies[:ERR_NICKNAMEINUSE] - handle_nick_taken - end - code = opts[:code].to_i - args = Marvin::Util.arguments(opts[:data]) - dispatch :incoming_numeric_processed, :code => code, :data => args - end + protected - def handle_welcome - logger.info "Say hello to my little friend - Got welcome" - # If a password is specified, we will attempt to message - # NickServ to identify ourselves. - say ":IDENTIFY #{self.configuration.password}", "NickServ" unless self.configuration.password.blank? - # Join the default channels IF they're already set - # Note that Marvin::IRC::Client.connect will set them AFTER this stuff is run. - self.default_channels.each { |c| self.join(c) } - end - - # The default handler for when a users nickname is taken on - # on the server. It will attempt to get the nicknickname from - # the nicknames part of the configuration (if available) and - # will then call #nick to change the nickname. - def handle_nick_taken - logger.info "Nickname '#{self.nickname}' on #{self.server} taken, trying next." - logger.info "Available Nicknames: #{self.nicks.empty? ? "None" : self.nicks.join(", ")}" - if !self.nicks.empty? - logger.info "Getting next nickname to switch" - next_nick = self.nicks.shift # Get the next nickname - logger.info "Attemping to set nickname to '#{next_nick}'" - nick next_nick - else - logger.fatal "No Nicknames available - QUITTING" - quit - end - end - - ## General IRC Functions - - # Sends a specified command to the server. - # Takes name (e.g. :privmsg) and all of the args. - # Very simply formats them as a string correctly - # and calls send_data with the results. - def command(name, *args) - # First, get the appropriate command - name = name.to_s.upcase - args = args.flatten.compact - irc_command = "#{name} #{args.join(" ").strip}\r\n" - send_line irc_command - end - - def join(channel) - channel = Marvin::Util.channel_name(channel) - # Record the fact we're entering the room. - # TODO: Refactor to only add the channel when we receive confirmation we've joined. - self.channels << channel - command :JOIN, channel - logger.info "Joined channel #{channel}" - dispatch :outgoing_join, :target => channel - end - - def part(channel, reason = nil) - channel = Marvin::Util.channel_name(channel) - if self.channels.include?(channel) - command :part, channel, Marvin::Util.last_param(reason) - dispatch :outgoing_part, :target => channel, :reason => reason - logger.info "Parted from room #{channel}#{reason ? " - #{reason}" : ""}" - else - logger.warn "Tried to disconnect from #{channel} - which you aren't a part of" - end - end - - def quit(reason = nil) - self.disconnect_expected = true - logger.info "Preparing to part from #{self.channels.size} channels" - self.channels.to_a.each do |chan| - logger.info "Parting from #{chan}" - self.part chan, reason - end - logger.info "Parted from all channels, quitting" - command :quit - dispatch :quit - # Remove the connections from the pool - self.connections.delete(self) - logger.info "Quit from server" - end - - def msg(target, message) - command :privmsg, target, Marvin::Util.last_param(message) - logger.info "Message sent to #{target} - #{message}" - dispatch :outgoing_message, :target => target, :message => message - end - - def action(target, message) - action_text = Marvin::Util.last_param "\01ACTION #{message.strip}\01" - command :privmsg, target, action_text - dispatch :outgoing_action, :target => target, :message => message - logger.info "Action sent to #{target} - #{message}" - end - - def pong(data) - command :pong, data - dispatch :outgoing_pong - logger.info "PONG sent to #{data}" - end - - def nick(new_nick) - logger.info "Changing nickname to #{new_nick}" - command :nick, new_nick - self.nickname = new_nick - dispatch :outgoing_nick, :new_nick => new_nick - logger.info "Nickname changed to #{new_nick}" + def util + Marvin::Util end end end \ No newline at end of file