marvin 0.8.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|