lib/marvin/abstract_client.rb in jeffrafter-marvin-0.1.20081115 vs lib/marvin/abstract_client.rb in jeffrafter-marvin-0.1.20081120
- old
+ new
@@ -5,12 +5,21 @@
module Marvin
class AbstractClient
include Marvin::Dispatchable
+ 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]
+ end
+
cattr_accessor :events, :configuration, :logger, :is_setup, :connections
- attr_accessor :channels, :nickname
+ attr_accessor :channels, :nickname, :server, :port, :nicks, :pass, :disconnect_expected, :original_opts
# Set the default values for the variables
self.events = []
self.configuration = OpenStruct.new
self.configuration.channels = []
@@ -20,23 +29,29 @@
# 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.debug "Initializing the current instance"
+ logger.info "Initializing the current instance"
self.channels = []
self.connections << self
- logger.debug "Setting the client for each handler"
+ logger.info "Setting the client for each handler"
self.handlers.each { |h| h.client = self if h.respond_to?(:client=) }
- logger.debug "Dispatching the default :client_connected event"
+ 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)
dispatch :client_disconnected
- Marvin::Loader.stop! if self.connections.blank?
+ unless self.disconnect_expected
+ logger.warn "Lost connection to server - adding reconnect"
+ self.class.add_reconnect self.original_opts
+ else
+ Marvin::Loader.stop! if self.connections.blank?
+ end
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.
@@ -49,15 +64,10 @@
# 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
return if self.is_setup
- # Default the logger back to a new one.
- self.configuration.channels ||= []
- unless self.configuration.channel.blank? || self.configuration.channels.include?(self.configuration.channel)
- self.configuration.channels.unshift(self.configuration.channel)
- end
if configuration.logger.blank?
require 'logger'
configuration.logger = Marvin::Logger.logger
end
self.logger = self.configuration.logger
@@ -78,44 +88,43 @@
# 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.debug "About to handle post init"
+ 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.debug "Setting default nickname"
- default_nickname = self.configuration.nick || self.configuration.nicknames.shift
+ logger.info "Setting default nickname"
+ default_nickname = self.nicks.shift
nick default_nickname
- logger.debug "sending user command"
+ logger.info "sending user command"
command :user, self.configuration.user, "0", "*", Marvin::Util.last_param(self.configuration.name)
- # 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
- self.configuration.channels.each { |c| self.join c }
rescue Exception => e
Marvin::ExceptionTracker.log(e)
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_incoming_nick_taken(opts = {})
- logger.info "Nick Is Taken"
- logger.debug "Available Nicknames: #{self.configuration.nicknames.to_a.join(", ")}"
- available_nicknames = self.configuration.nicknames.to_a
- if available_nicknames.length > 0
- logger.debug "Getting next nickname to switch"
- next_nick = available_nicknames.shift # Get the next nickname
- self.configuration.nicknames = available_nicknames
- logger.info "Attemping to set nickname to #{new_nick}"
- nick next_nick
- else
- logger.info "No Nicknames available - QUITTING"
- quit
+
+ def default_channels
+ @default_channels ||= []
+ end
+
+ def default_channels=(channels)
+ @default_channels = channels.to_a.map { |c| c.to_s }
+ 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_loaded
end
+ return @nicks
end
# The default response for PING's - it simply replies
# with a PONG.
def handle_incoming_ping(opts = {})
@@ -124,32 +133,67 @@
end
# 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}
+ dispatch :incoming_numeric_processed, :code => code, :data => args
end
+ 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"
+ 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
@@ -164,16 +208,17 @@
logger.warn "Tried to disconnect from #{channel} - which you aren't a part of"
end
end
def quit(reason = nil)
- logger.debug "Preparing to part from #{self.channels.size} channels"
+ self.disconnect_expected = true
+ logger.info "Preparing to part from #{self.channels.size} channels"
self.channels.to_a.each do |chan|
- logger.debug "Parting from #{chan}"
+ logger.info "Parting from #{chan}"
self.part chan, reason
end
- logger.debug "Parted from all channels, quitting"
- command :quit
+ 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
\ No newline at end of file