summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGiuseppe Bilotta <giuseppe.bilotta@gmail.com>2009-08-08 01:40:18 +0200
committerGiuseppe Bilotta <giuseppe.bilotta@gmail.com>2009-08-12 00:56:27 +0200
commita993d1c358e24ab85621568c10411c5496e2dea8 (patch)
treeacde205a3ed605476f6eabf6e04c0b0441a7e10f
parent74d7d02ed07740e50c3f87da9ecedf13397729ea (diff)
Better handling of MessageMapper failures
The plugin fallback method is now passed the list of failures. Failures themselves are encapsulated in their own data type, making it easier to handle failures that should inform the user with something more detailed than the classic 'usage' pattern. It's still up to the fallback method to make use (e.g. echo) the relevant messages.
-rw-r--r--lib/rbot/messagemapper.rb78
-rw-r--r--lib/rbot/plugins.rb9
2 files changed, 73 insertions, 14 deletions
diff --git a/lib/rbot/messagemapper.rb b/lib/rbot/messagemapper.rb
index c1691aff..ca076e4a 100644
--- a/lib/rbot/messagemapper.rb
+++ b/lib/rbot/messagemapper.rb
@@ -52,6 +52,57 @@ class Bot
# {:option => "bar", :otheroption => "baz"}
# See the #map method for more details.
class MessageMapper
+
+ class Failure
+ STRING = "template %{template} failed to recognize message %{message}"
+ FRIENDLY = "I failed to understand the command"
+ attr_reader :template
+ attr_reader :message
+ def initialize(tmpl, msg)
+ @template = tmpl
+ @message = msg
+ end
+
+ def to_s
+ STRING % {
+ :template => template.template,
+ :regexp => template.regexp,
+ :message => message.message,
+ :action => template.options[:action]
+ }
+ end
+ end
+
+ # failures with a friendly message
+ class FriendlyFailure < Failure
+ def friendly
+ self.class::FRIENDLY rescue FRIENDLY
+ end
+ end
+
+ class NotPrivateFailure < FriendlyFailure
+ STRING = "template %{template} is not configured for private messages"
+ FRIENDLY = "the command must not be given in private"
+ end
+
+ class NotPublicFailure < FriendlyFailure
+ STRING = "template %{template} is not configured for public messages"
+ FRIENDLY = "the command must not be given in public"
+ end
+
+ class NoMatchFailure < Failure
+ STRING = "%{message} does not match %{template} (%{regex})"
+ end
+
+ class PartialMatchFailure < Failure
+ STRING = "%{message} only matches %{template} (%{regex}) partially"
+ end
+
+ class NoActionFailure < FriendlyFailure
+ STRING = "%{template} calls undefined action %{action}"
+ FRIENDLY = "uh-ho, somebody forgot to tell me how to do that ..."
+ end
+
# used to set the method name used as a fallback for unmatched messages.
# The default fallback is a method called "usage".
attr_writer :fallback
@@ -194,13 +245,13 @@ class Bot
return false if @templates.empty?
failures = []
@templates.each do |tmpl|
- options, failure = tmpl.recognize(m)
- if options.nil?
- failures << [tmpl, failure]
+ options = tmpl.recognize(m)
+ if options.kind_of? Failure
+ failures << options
else
action = tmpl.options[:action]
unless @parent.respond_to?(action)
- failures << [tmpl, "class does not respond to action #{action}"]
+ failures << NoActionFailure.new(tmpl, m)
next
end
auth = tmpl.options[:full_auth_path]
@@ -228,13 +279,13 @@ class Bot
return false
end
end
- failures.each {|f, r|
- debug "#{f.inspect} => #{r}"
+ failures.each {|r|
+ debug "#{r.template.inspect} => #{r}"
}
debug "no handler found, trying fallback"
if @fallback && @parent.respond_to?(@fallback)
if m.bot.auth.allow?(@fallback, m.source, m.replyto)
- @parent.send(@fallback, m, {})
+ @parent.send(@fallback, m, {:failures => failures})
return true
end
end
@@ -531,13 +582,12 @@ class Bot
debug "Testing #{m.message.inspect} against #{self.inspect}"
- # Early out
- return nil, "template #{@template} is not configured for private messages" if @options.has_key?(:private) && !@options[:private] && m.private?
- return nil, "template #{@template} is not configured for public messages" if @options.has_key?(:public) && !@options[:public] && !m.private?
-
matching = @regexp.match(m.message)
- return nil, "#{m.message.inspect} doesn't match #{@template} (#{@regexp})" unless matching
- return nil, "#{m.message.inspect} only matches #{@template} (#{@regexp}) partially: #{matching[0].inspect}" unless matching[0] == m.message
+ return MessageMapper::NoMatchFailure.new(self, m) unless matching
+ return MessageMapper::PartialMatchFailure.new(self, m) unless matching[0] == m.message
+
+ return MessageMapper::NotPrivateFailure.new(self, m) if @options.has_key?(:private) && !@options[:private] && m.private?
+ return MessageMapper::NotPublicFailure.new(self, m) if @options.has_key?(:public) && !@options[:public] && !m.private?
debug_match = matching[1..-1].collect{ |d| d.inspect}.join(', ')
debug "#{m.message.inspect} matched #{@regexp} with #{debug_match}"
@@ -589,7 +639,7 @@ class Bot
}
options.delete_if {|k, v| v.nil?} # Remove nil values.
- return options, nil
+ return options
end
def inspect
diff --git a/lib/rbot/plugins.rb b/lib/rbot/plugins.rb
index e7905c50..2b4694ad 100644
--- a/lib/rbot/plugins.rb
+++ b/lib/rbot/plugins.rb
@@ -341,6 +341,15 @@ module Plugins
# MessageMapper uses 'usage' as its default fallback method.
#
def usage(m, params = {})
+ if params[:failures].respond_to? :find
+ friendly = params[:failures].find do |f|
+ f.kind_of? MessageMapper::FriendlyFailure
+ end
+ if friendly
+ m.reply friendly.friendly
+ return
+ end
+ end
m.reply(_("incorrect usage, ask for help using '%{command}'") % {:command => "#{@bot.nick}: help #{m.plugin}"})
end