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