Class: Marvin::AbstractClient
- Inherits:
-
Object
- Object
- Marvin::AbstractClient
- Defined in:
- lib/marvin/abstract_client.rb,
lib/marvin/client/actions.rb,
lib/marvin/client/default_handlers.rb
Overview
An abstract class implementing (and mixing in) a lot of the default functionality involved on handling an irc connection.
To provide an implementation, you must subclass and implement #send_line, .add_reconnect, and any other loader etc methods.
Direct Known Subclasses
Constant Summary collapse
- @@events =
Set the default values for the variables
[]
- @@configuration =
Marvin::Nash.new
- @@connections =
[]
- @@development =
false
Instance Attribute Summary collapse
-
#channels ⇒ Object
Returns the value of attribute channels.
-
#connection_config ⇒ Object
Returns the value of attribute connection_config.
-
#disconnect_expected ⇒ Object
Returns the value of attribute disconnect_expected.
-
#nickname ⇒ Object
Returns the value of attribute nickname.
-
#nicks ⇒ Array<String>
Returns a list available nicks / nicks to try on connect.
-
#pass ⇒ Object
Returns the value of attribute pass.
-
#port ⇒ Object
Returns the value of attribute port.
-
#server ⇒ Object
Returns the value of attribute server.
Class Method Summary collapse
-
.configuration=(config) ⇒ Object
Sets the current class-wide settings of this IRC Client to an instance of Marvin::Nash with the properties of a hash / nash passed in.
-
.configure {|an| ... } ⇒ Object
Configure the class - namely, merge the app-wide configuration and if given a block, merge the results in.
-
.setup ⇒ Object
Conditional configure.
-
.setup? ⇒ Boolean
Check if if the cient class has been setup yet.
Instance Method Summary collapse
-
#action(target, message) ⇒ Object
Does a CTCP action in a channel (equiv. to doing /me in most IRC clients) e.g.
-
#command(name, *args) ⇒ Object
Sends a specified command to the server.
-
#default_channels ⇒ Array<String>
Returns a list of default channels to join on connect.
-
#default_channels=(channels) ⇒ Object
Sets the list of default channels to join.
-
#handle_client_connected(opts = {}) ⇒ Object
The default handler for all things initialization-related on the client.
-
#handle_incoming_error(opts = {}) ⇒ Object
Make sure we show user server errors.
-
#handle_incoming_join(opts = {}) ⇒ Object
Only record joins when you’ve successfully joined the channel.
-
#handle_incoming_numeric(opts = {}) ⇒ Object
TODO: Get the correct mapping for a given Code.
-
#handle_incoming_ping(opts = {}) ⇒ Object
The default response for PING’s - it simply replies with a PONG.
-
#handle_nick_taken ⇒ Object
The default handler for when a users nickname is taken on on the server.
- #handle_welcome ⇒ Object
-
#host_with_port ⇒ String
Returns the irc server and port.
-
#initialize(opts) ⇒ AbstractClient
constructor
A new instance of AbstractClient.
-
#join(*channels_to_join) ⇒ Object
Join one or more channels on the current server e.g.
-
#msg(target, message) ⇒ Object
Sends a message to a target (either a channel or a user) e.g.
- #nick(new_nick) ⇒ Object
-
#part(channel, reason = nil) ⇒ Object
Parts a channel, with an optional reason e.g.
- #pong(data) ⇒ Object
-
#pre_dispatching ⇒ Object
Before dispatching, check if we need to reload and setup handlers correctly.
-
#process_connect ⇒ Object
Initializes the instance variables used for the current connection, dispatching a :client_connected event once it has finished.
-
#process_development ⇒ Object
If @@development is true, We’ll attempt to reload any changed files (namely, handlers).s.
-
#process_disconnect ⇒ Object
Handles a lost connection / disconnect, stopping the loader if it’s the last connection (purposely terminated) otherwise scheduling a reconnection.
-
#quit(reason = nil, part_before_quit = false) ⇒ Object
Quites from a server, first parting all channels if a second argument is passed as true e.g.
-
#receive_line(line) ⇒ Object
Receives a raw line (without EOL characters) and dispatch the incoming_line event.
-
#setup_handlers ⇒ Object
Iterates over handles and calls client= when defined.
Constructor Details
#initialize(opts) ⇒ AbstractClient
Returns a new instance of AbstractClient.
16 17 18 19 20 21 22 23 24 |
# File 'lib/marvin/abstract_client.rb', line 16 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 |
Instance Attribute Details
#channels ⇒ Object
Returns the value of attribute channels.
27 28 29 |
# File 'lib/marvin/abstract_client.rb', line 27 def channels @channels end |
#connection_config ⇒ Object
Returns the value of attribute connection_config.
27 28 29 |
# File 'lib/marvin/abstract_client.rb', line 27 def connection_config @connection_config end |
#disconnect_expected ⇒ Object
Returns the value of attribute disconnect_expected.
27 28 29 |
# File 'lib/marvin/abstract_client.rb', line 27 def disconnect_expected @disconnect_expected end |
#nickname ⇒ Object
Returns the value of attribute nickname.
27 28 29 |
# File 'lib/marvin/abstract_client.rb', line 27 def nickname @nickname end |
#nicks ⇒ Array<String>
Returns a list available nicks / nicks to try on connect.
164 165 166 |
# File 'lib/marvin/abstract_client.rb', line 164 def nicks @nicks end |
#pass ⇒ Object
Returns the value of attribute pass.
27 28 29 |
# File 'lib/marvin/abstract_client.rb', line 27 def pass @pass end |
#port ⇒ Object
Returns the value of attribute port.
27 28 29 |
# File 'lib/marvin/abstract_client.rb', line 27 def port @port end |
#server ⇒ Object
Returns the value of attribute server.
27 28 29 |
# File 'lib/marvin/abstract_client.rb', line 27 def server @server end |
Class Method Details
.configuration=(config) ⇒ Object
Sets the current class-wide settings of this IRC Client to an instance of Marvin::Nash with the properties of a hash / nash passed in.
The new configuration will be normalized before use (namely, it will convert nested items to nashes)
95 96 97 98 |
# File 'lib/marvin/abstract_client.rb', line 95 def self.configuration=(config) config = Marvin::Nash.new(config.to_hash) unless config.is_a?(Marvin::Nash) @@configuration = config.normalized end |
.configure {|an| ... } ⇒ Object
Configure the class - namely, merge the app-wide configuration and if given a block, merge the results in.
117 118 119 120 121 122 123 124 125 126 127 128 |
# File 'lib/marvin/abstract_client.rb', line 117 def self.configure config = Marvin::Nash.new config.merge! Marvin::Settings.configuration if block_given? yield(nash = Marvin::Nash.new) config.merge! nash end @@configuration = config # Help is only currently available on an instance NOT running the distributed handler. Marvin::CoreCommands.register! unless Marvin::Distributed::Handler.registered? @setup = true end |
.setup ⇒ Object
Conditional configure
107 108 109 110 |
# File 'lib/marvin/abstract_client.rb', line 107 def self.setup return if setup? configure end |
.setup? ⇒ Boolean
Check if if the cient class has been setup yet
102 103 104 |
# File 'lib/marvin/abstract_client.rb', line 102 def self.setup? @setup ||= false end |
Instance Method Details
#action(target, message) ⇒ Object
Does a CTCP action in a channel (equiv. to doing /me in most IRC clients) e.g.
action "#marvin-testing", "is about to sleep"
action "SuttoL", "is about to sleep"
83 84 85 86 87 |
# File 'lib/marvin/client/actions.rb', line 83 def action(target, ) command :privmsg, target, "\01ACTION #{.strip}\01" dispatch :outgoing_action, :target => target, :message => logger.info "Action sent to #{target} - #{}" end |
#command(name, *args) ⇒ Object
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.
10 11 12 13 14 15 16 |
# File 'lib/marvin/client/actions.rb', line 10 def command(name, *args) # First, get the appropriate command name = name.to_s.upcase args = args.flatten args << util.last_param(args.pop) send_line "#{name} #{args.compact.join(" ").strip}\r\n" end |
#default_channels ⇒ Array<String>
Returns a list of default channels to join on connect
145 146 147 |
# File 'lib/marvin/abstract_client.rb', line 145 def default_channels @default_channels ||= [] end |
#default_channels=(channels) ⇒ Object
Sets the list of default channels to join
151 152 153 |
# File 'lib/marvin/abstract_client.rb', line 151 def default_channels=(channels) @default_channels = channels.to_a.map { |c| c.to_s } end |
#handle_client_connected(opts = {}) ⇒ Object
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.
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
# File 'lib/marvin/client/default_handlers.rb', line 11 def handle_client_connected(opts = {}) logger.info "About to handle client connected" # If the pass is set unless pass.blank? logger.info "Sending pass for connection" command :pass, pass end # IRC Connection is establish so we send all the required commands to the server. logger.info "Setting default nickname" nick nicks.shift logger.info "Sending user command" command :user, configuration.user, "0", "*", configuration.name rescue Exception => e Marvin::ExceptionTracker.log(e) end |
#handle_incoming_error(opts = {}) ⇒ Object
Make sure we show user server errors
88 89 90 91 92 |
# File 'lib/marvin/client/default_handlers.rb', line 88 def handle_incoming_error(opts = {}) if opts[:message].present? logger.error "Server ERROR Message: #{opts[:message]}" end end |
#handle_incoming_join(opts = {}) ⇒ Object
Only record joins when you’ve successfully joined the channel.
80 81 82 83 84 85 |
# File 'lib/marvin/client/default_handlers.rb', line 80 def handle_incoming_join(opts = {}) if opts[:nick] == @nickname channels << opts[:target] logger.info "Successfully joined channel #{opts[:target]}" end end |
#handle_incoming_numeric(opts = {}) ⇒ Object
TODO: Get the correct mapping for a given Code.
39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/marvin/client/default_handlers.rb', line 39 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 |
#handle_incoming_ping(opts = {}) ⇒ Object
The default response for PING’s - it simply replies with a PONG.
32 33 34 35 |
# File 'lib/marvin/client/default_handlers.rb', line 32 def handle_incoming_ping(opts = {}) logger.info "Received Incoming Ping - Handling with a PONG" pong(opts[:data]) end |
#handle_nick_taken ⇒ Object
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.
65 66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/marvin/client/default_handlers.rb', line 65 def handle_nick_taken logger.info "Nickname '#{nickname}' on #{server} taken, trying next." logger.info "Available Nicknames: #{nicks.empty? ? "None" : nicks.join(", ")}" if !nicks.empty? logger.info "Getting next nickname to switch" next_nick = nicks.shift # Get the next nickname logger.info "Attemping to set nickname to '#{next_nick}'" nick next_nick else logger.error "No Nicknames available - QUITTING" quit end end |
#handle_welcome ⇒ Object
51 52 53 54 55 56 57 58 59 |
# File 'lib/marvin/client/default_handlers.rb', line 51 def handle_welcome logger.info "Welcome received from server" # If a password is specified, we will attempt to message # NickServ to identify ourselves. say ":IDENTIFY #{self.configuration.password}", "NickServ" if configuration.password.present? # Join the default channels IF they're already set # Note that Marvin::IRC::Client.connect will set them AFTER this stuff is run. join default_channels end |
#host_with_port ⇒ String
Returns the irc server and port
157 158 159 |
# File 'lib/marvin/abstract_client.rb', line 157 def host_with_port @host_with_port ||= "#{server}:#{port}" end |
#join(*channels_to_join) ⇒ Object
Join one or more channels on the current server e.g.
client.join "#marvin-testing"
client.join ["#marvin-testing", "#rubyonrails"]
client.join "#marvin-testing", "#rubyonrails"
23 24 25 26 27 28 29 |
# File 'lib/marvin/client/actions.rb', line 23 def join(*channels_to_join) channels_to_join = channels_to_join.flatten.map { |c| util.channel_name(c) } # If you're joining multiple channels at once, we join them together command :JOIN, channels_to_join.join(",") channels_to_join.each { |channel| dispatch :outgoing_join, :target => channel } logger.info "Sent JOIN for channels #{channels_to_join.join(", ")}" end |
#msg(target, message) ⇒ Object
Sends a message to a target (either a channel or a user) e.g.
msg "#marvin-testing", "Hello there!"
msg "SuttoL", "Hey, I'm playing with marvin!"
73 74 75 76 77 |
# File 'lib/marvin/client/actions.rb', line 73 def msg(target, ) command :privmsg, target, dispatch :outgoing_message, :target => target, :message => logger.info "Message #{target} - #{}" end |
#nick(new_nick) ⇒ Object
95 96 97 98 99 100 101 |
# File 'lib/marvin/client/actions.rb', line 95 def nick(new_nick) logger.info "Changing nick to #{new_nick}" command :nick, new_nick @nickname = new_nick dispatch :outgoing_nick, :new_nick => new_nick logger.info "Nick changed to #{new_nick}" end |
#part(channel, reason = nil) ⇒ Object
Parts a channel, with an optional reason e.g.
part "#marvin-testing"
part "#marvin-testing", "Ninjas stole by felafel"
35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/marvin/client/actions.rb', line 35 def part(channel, reason = nil) channel = util.channel_name(channel) # Send the command anyway, even if we're not a # a recorded member something might of happened. command :part, channel, reason if channels.include?(channel) dispatch :outgoing_part, :target => channel, :reason => reason logger.info "Parted channel #{channel} - #{reason.present? ? reason : "Non given"}" else logger.warn "Parted channel #{channel} but wasn't recorded as member of channel" end end |
#pong(data) ⇒ Object
89 90 91 92 93 |
# File 'lib/marvin/client/actions.rb', line 89 def pong(data) command :pong, data dispatch :outgoing_pong logger.info "PONG sent to #{host_with_port} w/ data - #{data}" end |
#pre_dispatching ⇒ Object
Before dispatching, check if we need to reload and setup handlers correctly.
82 83 84 85 |
# File 'lib/marvin/abstract_client.rb', line 82 def pre_dispatching process_development setup_handlers end |
#process_connect ⇒ Object
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.
43 44 45 46 47 48 49 50 |
# File 'lib/marvin/abstract_client.rb', line 43 def process_connect self.class.setup logger.info "Initializing the current instance" @channels = [] connections << self logger.info "Dispatching the default :client_connected event" dispatch :client_connected end |
#process_development ⇒ Object
If @@development is true, We’ll attempt to reload any changed files (namely, handlers).s
77 78 79 |
# File 'lib/marvin/abstract_client.rb', line 77 def process_development Marvin::Reloading.reload! if @@development end |
#process_disconnect ⇒ Object
Handles a lost connection / disconnect, stopping the loader if it’s the last connection (purposely terminated) otherwise scheduling a reconnection
56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/marvin/abstract_client.rb', line 56 def process_disconnect logger.info "Handling disconnect for #{host_with_port}" connections.delete(self) dispatch :client_disconnected if @disconnect_expected Marvin::Loader.stop! if connections.blank? else logger.warn "Unexpectly lost connection to server; adding reconnect" self.class.add_reconnect @connection_config end end |
#quit(reason = nil, part_before_quit = false) ⇒ Object
Quites from a server, first parting all channels if a second argument is passed as true e.g.
quit
quit "Going to grab some z's"
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
# File 'lib/marvin/client/actions.rb', line 53 def quit(reason = nil, part_before_quit = false) @disconnect_expected = true # If the user wants to part before quitting, they should # pass a second, true, parameter if part_before_quit logger.info "Preparing to part from channels before quitting" channels.to_a.each { |chan| part(chan, reason) } logger.info "Parted from all channels, quitting" end command :quit, reason dispatch :outgoing_quit # Remove the connections from the pool connections.delete(self) logger.info "Quit from #{host_with_port}" end |
#receive_line(line) ⇒ Object
Receives a raw line (without EOL characters) and dispatch the incoming_line event. Once that’s done, parse the line and dispatch an event if present.
137 138 139 140 141 |
# File 'lib/marvin/abstract_client.rb', line 137 def receive_line(line) dispatch :incoming_line, :line => line event = Marvin::Settings.parser.parse(line) dispatch(event.to_incoming_event_name, event.to_hash) unless event.nil? end |
#setup_handlers ⇒ Object
Iterates over handles and calls client= when defined. Used before dispatching in order to ensure each hander has the correct client. Note that this needs to be improved soon.
71 72 73 |
# File 'lib/marvin/abstract_client.rb', line 71 def setup_handlers handlers.each { |h| h.client = self if h.respond_to?(:client=) } end |