marvin 0.8.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/marvin +33 -0
- data/handlers/debug_handler.rb +5 -0
- data/handlers/hello_world.rb +9 -0
- data/handlers/keiki_thwopper.rb +21 -0
- data/handlers/simple_logger.rb +24 -0
- data/handlers/tweet_tweet.rb +19 -0
- data/lib/marvin.rb +56 -0
- data/lib/marvin/abstract_client.rb +146 -0
- data/lib/marvin/abstract_parser.rb +29 -0
- data/lib/marvin/base.rb +195 -0
- data/lib/marvin/client/actions.rb +104 -0
- data/lib/marvin/client/default_handlers.rb +97 -0
- data/lib/marvin/command_handler.rb +91 -0
- data/lib/marvin/console.rb +50 -0
- data/lib/marvin/core_commands.rb +49 -0
- data/lib/marvin/distributed.rb +8 -0
- data/lib/marvin/distributed/client.rb +225 -0
- data/lib/marvin/distributed/handler.rb +85 -0
- data/lib/marvin/distributed/protocol.rb +88 -0
- data/lib/marvin/distributed/server.rb +154 -0
- data/lib/marvin/dsl.rb +103 -0
- data/lib/marvin/exception_tracker.rb +19 -0
- data/lib/marvin/exceptions.rb +11 -0
- data/lib/marvin/irc.rb +7 -0
- data/lib/marvin/irc/client.rb +168 -0
- data/lib/marvin/irc/event.rb +39 -0
- data/lib/marvin/irc/replies.rb +154 -0
- data/lib/marvin/logging_handler.rb +76 -0
- data/lib/marvin/middle_man.rb +103 -0
- data/lib/marvin/parsers.rb +9 -0
- data/lib/marvin/parsers/command.rb +107 -0
- data/lib/marvin/parsers/prefixes.rb +8 -0
- data/lib/marvin/parsers/prefixes/host_mask.rb +35 -0
- data/lib/marvin/parsers/prefixes/server.rb +24 -0
- data/lib/marvin/parsers/ragel_parser.rb +720 -0
- data/lib/marvin/parsers/ragel_parser.rl +143 -0
- data/lib/marvin/parsers/simple_parser.rb +35 -0
- data/lib/marvin/settings.rb +31 -0
- data/lib/marvin/test_client.rb +58 -0
- data/lib/marvin/util.rb +54 -0
- data/templates/boot.erb +3 -0
- data/templates/connections.yml.erb +10 -0
- data/templates/debug_handler.erb +5 -0
- data/templates/hello_world.erb +10 -0
- data/templates/rakefile.erb +15 -0
- data/templates/settings.yml.erb +8 -0
- data/templates/setup.erb +31 -0
- data/templates/test_helper.erb +17 -0
- data/test/abstract_client_test.rb +63 -0
- data/test/parser_comparison.rb +62 -0
- data/test/parser_test.rb +266 -0
- data/test/test_helper.rb +62 -0
- data/test/util_test.rb +57 -0
- metadata +136 -0
data/bin/marvin
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'rubygems'
|
3
|
+
require File.join(File.dirname(__FILE__), "..", "lib", "marvin")
|
4
|
+
|
5
|
+
Marvin::Application.processing(ARGV) do |a|
|
6
|
+
|
7
|
+
a.banner = "Marvin v#{Marvin::VERSION} - An IRC Library for Ruby"
|
8
|
+
|
9
|
+
a.generator!
|
10
|
+
|
11
|
+
a.option :development, "Runs the app in development mode (handler reloading)", :shortcut => "D"
|
12
|
+
a.controller! :client, "Starts the actual Marvin client instance"
|
13
|
+
a.controller! :console, "Opens a friendly IRB prompt with Marvin pre-loaded"
|
14
|
+
a.controller! :distributed_client, "Starts a distributed client instance"
|
15
|
+
|
16
|
+
a.option :force, "force the creation of the application"
|
17
|
+
a.add "create PATH", "Creates a marvin application at the given location" do |path, options|
|
18
|
+
path = File.expand_path(path)
|
19
|
+
if File.exists?(path) && !options[:force]
|
20
|
+
die! "The path you tried to use, #{path}, already exists. Please try another or use the --force option"
|
21
|
+
end
|
22
|
+
setup_generator(path)
|
23
|
+
folders 'tmp', 'config', 'lib', 'handlers', 'test'
|
24
|
+
template 'boot.erb', 'config/boot.rb'
|
25
|
+
template 'setup.erb', 'config/setup.rb'
|
26
|
+
template 'settings.yml.erb', 'config/settings.yml'
|
27
|
+
template 'connections.yml.erb', 'config/connections.yml'
|
28
|
+
template 'debug_handler.erb', 'handlers/debug_handler.rb'
|
29
|
+
template 'hello_world.erb', 'handlers/hello_world.rb'
|
30
|
+
template 'rakefile.erb', 'Rakefile'
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class KeikiThwopper < Marvin::Base
|
2
|
+
|
3
|
+
FROM_REGEXP = /^(sutto)/i
|
4
|
+
THWOP_REGEXP = /(t+h+w+o+m*p+|stab|kill)/i
|
5
|
+
|
6
|
+
MESSAGES = [
|
7
|
+
"mwahahahaha",
|
8
|
+
"you totally deserved it",
|
9
|
+
"oi! leave 'em alone!",
|
10
|
+
"say hello to my little friend",
|
11
|
+
"you know, they could have liked that?"
|
12
|
+
]
|
13
|
+
|
14
|
+
on_event :incoming_action, :thwop_back
|
15
|
+
|
16
|
+
def thwop_back
|
17
|
+
return if !from || from !~ FROM_REGEXP || options.message !~ THWOP_REGEXP
|
18
|
+
action "#{$1}s #{from} (#{MESSAGES[rand(MESSAGES.length)]})"
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# A reference logger example
|
2
|
+
class SimpleLogger < Marvin::LoggingHandler
|
3
|
+
|
4
|
+
def setup_logging
|
5
|
+
logger.warn "Setting up the client"
|
6
|
+
end
|
7
|
+
|
8
|
+
def teardown_logging
|
9
|
+
logger.warn "Tearing down the logger"
|
10
|
+
end
|
11
|
+
|
12
|
+
def log_incoming(server, nick, target, message)
|
13
|
+
logger.fatal "[INCOMING] #{server} (#{target}) #{nick}: #{message}"
|
14
|
+
end
|
15
|
+
|
16
|
+
def log_outgoing(server, nick, target, message)
|
17
|
+
logger.fatal "[OUTGOING] #{server} (#{target}) #{nick}: #{message}"
|
18
|
+
end
|
19
|
+
|
20
|
+
def log_message(server, nick, target, message)
|
21
|
+
logger.fatal "[MESSAGE] #{server} (#{target}) #{nick}: #{message}"
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# Not Yet Complete: Twitter Client in Channel.
|
2
|
+
class TweetTweet < Marvin::Base
|
3
|
+
|
4
|
+
on_event :client_connected do
|
5
|
+
start_tweeting
|
6
|
+
end
|
7
|
+
|
8
|
+
def start_tweeting
|
9
|
+
client.periodically 180, :check_tweets
|
10
|
+
end
|
11
|
+
|
12
|
+
def handle_check_tweets
|
13
|
+
logger.debug ">> Check Tweets"
|
14
|
+
end
|
15
|
+
|
16
|
+
def show_tweet(tweet)
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
data/lib/marvin.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
2
|
+
require 'rubygems'
|
3
|
+
require 'perennial'
|
4
|
+
|
5
|
+
module Marvin
|
6
|
+
include Perennial
|
7
|
+
|
8
|
+
VERSION = [0, 8, 0, 0]
|
9
|
+
|
10
|
+
# Misc.
|
11
|
+
#autoload :Util, 'marvin/util'
|
12
|
+
# Client
|
13
|
+
#autoload :AbstractClient, 'marvin/abstract_client'
|
14
|
+
#autoload :IRC, 'marvin/irc'
|
15
|
+
autoload :TestClient, 'marvin/test_client'
|
16
|
+
# Console of DOOM.
|
17
|
+
autoload :Console, 'marvin/console'
|
18
|
+
# Distributed
|
19
|
+
autoload :Distributed, 'marvin/distributed'
|
20
|
+
autoload :Status, 'marvin/status'
|
21
|
+
# Handler
|
22
|
+
autoload :Base, 'marvin/base'
|
23
|
+
autoload :CommandHandler, 'marvin/command_handler'
|
24
|
+
autoload :LoggingHandler, 'marvin/logging_handler'
|
25
|
+
autoload :CoreCommands, 'marvin/core_commands'
|
26
|
+
autoload :MiddleMan, 'marvin/middle_man'
|
27
|
+
# These should be namespaced under IRC
|
28
|
+
#autoload :AbstractParser, 'marvin/abstract_parser'
|
29
|
+
autoload :Parsers, 'marvin/parsers'
|
30
|
+
|
31
|
+
|
32
|
+
manifest do |m, l|
|
33
|
+
Settings.root = File.dirname(File.dirname(__FILE__))
|
34
|
+
l.register_controller :client, 'Marvin::Settings.client'
|
35
|
+
l.register_controller :console, 'Marvin::Console'
|
36
|
+
l.register_controller :distributed_client, 'Marvin::Distributed::Client'
|
37
|
+
# Core Commands handily makes available a set
|
38
|
+
# of information about what is running etc.
|
39
|
+
|
40
|
+
l.before_run do
|
41
|
+
if l.distributed_client?
|
42
|
+
Marvin::Settings.client = Marvin::Distributed::Client
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.version(include_minor = false)
|
49
|
+
VERSION[0, (include_minor ? 4 : 3)].join(".")
|
50
|
+
end
|
51
|
+
|
52
|
+
has_library :util, :abstract_client, :abstract_parser, :irc, :exception_tracker
|
53
|
+
|
54
|
+
extends_library :settings
|
55
|
+
|
56
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
require "marvin/irc/event"
|
2
|
+
|
3
|
+
module Marvin
|
4
|
+
class AbstractClient
|
5
|
+
|
6
|
+
is :dispatchable, :loggable
|
7
|
+
|
8
|
+
def initialize(opts)
|
9
|
+
opts = opts.to_nash if opts.is_a?(Hash)
|
10
|
+
@connection_config = opts.dup # Copy the options so we can use them to reconnect.
|
11
|
+
@server = opts.server
|
12
|
+
@port = opts.port
|
13
|
+
@default_channels = opts.channels
|
14
|
+
@nicks = opts.nicks || []
|
15
|
+
@pass = opts.pass
|
16
|
+
end
|
17
|
+
|
18
|
+
cattr_accessor :events, :configuration, :is_setup, :connections, :development
|
19
|
+
attr_accessor :channels, :nickname, :server, :port, :nicks, :pass,
|
20
|
+
:disconnect_expected, :connection_config
|
21
|
+
|
22
|
+
# Set the default values for the variables
|
23
|
+
@@events = []
|
24
|
+
@@configuration = Marvin::Nash.new
|
25
|
+
@@connections = []
|
26
|
+
@@development = false
|
27
|
+
|
28
|
+
# Initializes the instance variables used for the
|
29
|
+
# current connection, dispatching a :client_connected event
|
30
|
+
# once it has finished. During this process, it will
|
31
|
+
# call #client= on each handler if they respond to it.
|
32
|
+
def process_connect
|
33
|
+
self.class.setup
|
34
|
+
logger.info "Initializing the current instance"
|
35
|
+
@channels = []
|
36
|
+
connections << self
|
37
|
+
logger.info "Setting the client for each handler"
|
38
|
+
setup_handlers
|
39
|
+
logger.info "Dispatching the default :client_connected event"
|
40
|
+
dispatch :client_connected
|
41
|
+
end
|
42
|
+
|
43
|
+
def process_disconnect
|
44
|
+
logger.info "Handling disconnect for #{host_with_port}"
|
45
|
+
connections.delete(self)
|
46
|
+
dispatch :client_disconnected
|
47
|
+
unless @disconnect_expected
|
48
|
+
logger.warn "Unexpectly lost connection to server; adding reconnect"
|
49
|
+
self.class.add_reconnect @connection_config
|
50
|
+
else
|
51
|
+
Marvin::Loader.stop! if connections.blank?
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def setup_handlers
|
56
|
+
handlers.each { |h| h.client = self if h.respond_to?(:client=) }
|
57
|
+
end
|
58
|
+
|
59
|
+
def process_development
|
60
|
+
if @@development
|
61
|
+
Marvin::Reloading.reload!
|
62
|
+
setup_handlers
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def dispatch(*args)
|
67
|
+
process_development
|
68
|
+
super
|
69
|
+
end
|
70
|
+
|
71
|
+
# Sets the current class-wide settings of this IRC Client
|
72
|
+
# to either an OpenStruct or the results of #to_hash on
|
73
|
+
# any other value that is passed in.
|
74
|
+
def self.configuration=(config)
|
75
|
+
config = Marvin::Nash.new(config.to_hash) unless config.is_a?(Marvin::Nash)
|
76
|
+
@@configuration = config.normalized
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.setup?
|
80
|
+
@setup ||= false
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.setup
|
84
|
+
return if setup?
|
85
|
+
configure
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.configure
|
89
|
+
config = Marvin::Nash.new
|
90
|
+
config.merge! Marvin::Settings.configuration
|
91
|
+
if block_given?
|
92
|
+
yield(nash = Marvin::Nash.new)
|
93
|
+
config.merge! nash
|
94
|
+
end
|
95
|
+
@@configuration = config
|
96
|
+
# Help is only currently available on an instance running
|
97
|
+
# distributed handler.
|
98
|
+
Marvin::CoreCommands.register! unless Marvin::Distributed::Handler.registered?
|
99
|
+
@setup = true
|
100
|
+
end
|
101
|
+
|
102
|
+
## Handling all of the the actual client stuff.
|
103
|
+
|
104
|
+
def receive_line(line)
|
105
|
+
dispatch :incoming_line, :line => line
|
106
|
+
event = Marvin::Settings.parser.parse(line)
|
107
|
+
dispatch(event.to_incoming_event_name, event.to_hash) unless event.nil?
|
108
|
+
end
|
109
|
+
|
110
|
+
def default_channels
|
111
|
+
@default_channels ||= []
|
112
|
+
end
|
113
|
+
|
114
|
+
def default_channels=(channels)
|
115
|
+
@default_channels = channels.to_a.map { |c| c.to_s }
|
116
|
+
end
|
117
|
+
|
118
|
+
def host_with_port
|
119
|
+
@host_with_port ||= "#{server}:#{port}"
|
120
|
+
end
|
121
|
+
|
122
|
+
def nicks
|
123
|
+
if @nicks.blank? && !@nicks_loaded
|
124
|
+
logger.info "Setting default nick list"
|
125
|
+
@nicks = []
|
126
|
+
@nicks << configuration.nick if configuration.nick?
|
127
|
+
@nicks += configuration.nicks.to_a if configuration.nicks?
|
128
|
+
@nicks.compact!
|
129
|
+
raise "No initial nicks for #{host_with_port}" if @nicks.blank?
|
130
|
+
@nicks_loaded = true
|
131
|
+
end
|
132
|
+
return @nicks
|
133
|
+
end
|
134
|
+
|
135
|
+
# Break it down into a couple of different files.
|
136
|
+
require 'marvin/client/default_handlers'
|
137
|
+
require 'marvin/client/actions'
|
138
|
+
|
139
|
+
protected
|
140
|
+
|
141
|
+
def util
|
142
|
+
Marvin::Util
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
146
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Marvin
|
2
|
+
class AbstractParser
|
3
|
+
|
4
|
+
attr_accessor :line, :command, :event
|
5
|
+
|
6
|
+
# Instantiates a parser instance, attempts to
|
7
|
+
# parse it for it's command and it's event.
|
8
|
+
def initialize(line)
|
9
|
+
@line = line
|
10
|
+
@command = self.class.parse!(line)
|
11
|
+
@event = @command.to_event unless @command.blank?
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_event
|
15
|
+
@event
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.parse(line)
|
19
|
+
new(line.strip).to_event
|
20
|
+
end
|
21
|
+
|
22
|
+
protected
|
23
|
+
|
24
|
+
def self.parse!(line)
|
25
|
+
raise NotImplementedError, "Must be implemented in a subclass"
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
data/lib/marvin/base.rb
ADDED
@@ -0,0 +1,195 @@
|
|
1
|
+
module Marvin
|
2
|
+
|
3
|
+
def self.handler_parent_classes
|
4
|
+
@@handler_parent_classes ||= Hash.new { |h,k| h[k] = Set.new }
|
5
|
+
end
|
6
|
+
|
7
|
+
class Base
|
8
|
+
is :loggable
|
9
|
+
|
10
|
+
@@handlers = Hash.new do |h,k|
|
11
|
+
h[k] = Hash.new { |h2, k2| h2[k2] = [] }
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_accessor :client, :target, :from, :options
|
15
|
+
|
16
|
+
class << self
|
17
|
+
|
18
|
+
def registered?
|
19
|
+
@registered ||= false
|
20
|
+
end
|
21
|
+
|
22
|
+
def registered=(value)
|
23
|
+
@registered = !!value
|
24
|
+
end
|
25
|
+
|
26
|
+
# Returns an array of all handlers associated with
|
27
|
+
# a specific event name (e.g. :incoming_message)
|
28
|
+
def event_handlers_for(message_name)
|
29
|
+
message_name = message_name.to_sym
|
30
|
+
items = []
|
31
|
+
klass = self
|
32
|
+
while klass != Object
|
33
|
+
items += @@handlers[klass][message_name]
|
34
|
+
klass = klass.superclass
|
35
|
+
end
|
36
|
+
items
|
37
|
+
end
|
38
|
+
|
39
|
+
# Registers a block to be used as an event handler. The first
|
40
|
+
# argument is always the name of the event and the second
|
41
|
+
# is either a method name (e.g. :my_awesome_method) or
|
42
|
+
# a block (which is instance_evaled)
|
43
|
+
def on_event(name, method_name = nil, &blk)
|
44
|
+
blk = proc { self.send(method_name) } if method_name.present?
|
45
|
+
@@handlers[self][name] << blk
|
46
|
+
end
|
47
|
+
|
48
|
+
# Like on_event but instead of taking an event name it takes
|
49
|
+
# either a number or a name - corresponding to an IRC numeric
|
50
|
+
# reply.
|
51
|
+
def on_numeric(value, method_name = nil, &blk)
|
52
|
+
value = value.is_a?(Numeric) ? ("%03d" % value) : Marvin::IRC::Replies[value]
|
53
|
+
on_event(:"incoming_numeric_#{new_value}", method_name, &blk) if value.present?
|
54
|
+
end
|
55
|
+
|
56
|
+
# Register this specific handler on the IRC handler.
|
57
|
+
def register!(parent = Marvin::Settings.client)
|
58
|
+
return if self == Marvin::Base # Only do it for sub-classes.
|
59
|
+
parent.register_handler self.new unless parent.handlers.any? { |h| h.class == self }
|
60
|
+
Marvin.handler_parent_classes[self.name] << parent
|
61
|
+
end
|
62
|
+
|
63
|
+
def reloading!
|
64
|
+
Marvin.handler_parent_classes[self.name].each do |dispatcher|
|
65
|
+
parent_handlers = dispatcher.handlers
|
66
|
+
related = parent_handlers.select { |h| h.class == self }
|
67
|
+
related.each do |h|
|
68
|
+
h.handle(:reloading, {})
|
69
|
+
dispatcher.delete_handler(h)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def reloaded!
|
75
|
+
Marvin.handler_parent_classes[self.name].each do |dispatcher|
|
76
|
+
before = dispatcher.handlers
|
77
|
+
register!(dispatcher)
|
78
|
+
after = dispatcher.handlers
|
79
|
+
(after - before).each { |h| h.handle(:reloaded, {}) }
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
def handle(message, options)
|
87
|
+
dup._handle(message, options)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Given an incoming message, handle it appropriately by getting all
|
91
|
+
# associated event handlers. It also logs any exceptions (aslong as
|
92
|
+
# they raised by halt)
|
93
|
+
def _handle(message, options)
|
94
|
+
setup_details(options)
|
95
|
+
h = self.class.event_handlers_for(message)
|
96
|
+
h.each { |eh| self.instance_eval(&eh) }
|
97
|
+
rescue Exception => e
|
98
|
+
# Pass on halt_handler_processing events.
|
99
|
+
raise e if e.is_a?(Marvin::HaltHandlerProcessing)
|
100
|
+
logger.fatal "Exception processing handler for #{message.inspect}"
|
101
|
+
Marvin::ExceptionTracker.log(e)
|
102
|
+
ensure
|
103
|
+
reset_details
|
104
|
+
end
|
105
|
+
|
106
|
+
# The default handler for numerics. mutates them into a more
|
107
|
+
# friendly version of themselves. It will also pass through
|
108
|
+
# the original incoming_numeric event.
|
109
|
+
def handle_incoming_numeric(opts)
|
110
|
+
handle(:incoming_numeric, opts)
|
111
|
+
handle(:"incoming_numeric_#{opts[:code]}", opts)
|
112
|
+
end
|
113
|
+
|
114
|
+
# msg sends the given text to the current target, be it
|
115
|
+
# either a channel or a specific user.
|
116
|
+
def msg(message, target = self.target)
|
117
|
+
client.msg(target, message)
|
118
|
+
end
|
119
|
+
|
120
|
+
alias say msg
|
121
|
+
|
122
|
+
def action(message, target = self.target)
|
123
|
+
client.action(target, message)
|
124
|
+
end
|
125
|
+
|
126
|
+
# A conditional version of message that will only send the message
|
127
|
+
# if the target / from is a user. To do this, it uses from_channel?
|
128
|
+
def pm(message, target)
|
129
|
+
say(message, target) unless from_channel?(target)
|
130
|
+
end
|
131
|
+
|
132
|
+
# Replies to a message. if it was received in a channel, it will
|
133
|
+
# use the standard irc "Name: text" convention for replying whilst
|
134
|
+
# if it was in a direct message it sends it as is.
|
135
|
+
def reply(message)
|
136
|
+
if from_channel?
|
137
|
+
say("#{from}: #{message}")
|
138
|
+
else
|
139
|
+
say(message, from)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def ctcp(message)
|
144
|
+
say("\01#{message}\01", from) if !from_channel?
|
145
|
+
end
|
146
|
+
|
147
|
+
# Request information
|
148
|
+
|
149
|
+
# reflects whether or not the current message / previous message came
|
150
|
+
# from a user via pm.
|
151
|
+
def from_user?
|
152
|
+
!from_channel?
|
153
|
+
end
|
154
|
+
|
155
|
+
# Determines whether a given target (defaulting to the target of the
|
156
|
+
# last message was in a channel)
|
157
|
+
def from_channel?(target = self.target)
|
158
|
+
target.present? && target =~ /^[\&\#]/
|
159
|
+
end
|
160
|
+
|
161
|
+
def addressed?
|
162
|
+
from_user? || options.message =~ /^#{client.nickname.downcase}:\s+/i
|
163
|
+
end
|
164
|
+
|
165
|
+
# A Perennial automagical helper for dispatch
|
166
|
+
def registered=(value)
|
167
|
+
self.class.registered = value
|
168
|
+
end
|
169
|
+
|
170
|
+
protected
|
171
|
+
|
172
|
+
# Initializes details for the current cycle - in essence, this makes the
|
173
|
+
# details of the current request available.
|
174
|
+
def setup_details(options)
|
175
|
+
@options = options.is_a?(Marvin::Nash) ? options : Marvin::Nash.new(options.to_hash)
|
176
|
+
@target = @options.target if @options.target?
|
177
|
+
@from = @options.nick if @options.nick?
|
178
|
+
end
|
179
|
+
|
180
|
+
def reset_details
|
181
|
+
@options = nil
|
182
|
+
@target = nil
|
183
|
+
@from = nil
|
184
|
+
end
|
185
|
+
|
186
|
+
# Halt can be called during the handle / process. Doing so
|
187
|
+
# prevents any more handlers in the handler chain from being
|
188
|
+
# called. It's kind of like return but it works across all
|
189
|
+
# handlers, not just the current one.
|
190
|
+
def halt!
|
191
|
+
raise Marvin::HaltHandlerProcessing
|
192
|
+
end
|
193
|
+
|
194
|
+
end
|
195
|
+
end
|