X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=lib%2Frbot%2Fmessagemapper.rb;h=c222ee71350066e943c25a65f94cf5f745a04b50;hb=87d2affc0fbe7cf864fdad4eca506d9d06a0de0c;hp=2b9fc5e32c2a99238a79c53b469d927b9e9804b2;hpb=a41182b02b0b4f70e0a4e4ffff9c831207a87ba5;p=user%2Fhenk%2Fcode%2Fruby%2Frbot.git diff --git a/lib/rbot/messagemapper.rb b/lib/rbot/messagemapper.rb index 2b9fc5e3..c222ee71 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 @@ -144,7 +195,7 @@ class Bot # threaded:: # a boolean (defaults to false) that determines whether the action should be # called in a separate thread. - # + # # # Further examples: # @@ -194,20 +245,20 @@ 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] debug "checking auth for #{auth}" if m.bot.auth.allow?(auth, m.source, m.replyto) debug "template match found and auth'd: #{action.inspect} #{options.inspect}" - if m.thread || (m.thread == nil && tmpl.options[:thread] || tmpl.options[:threaded]) + if !m.in_thread && (tmpl.options[:thread] || tmpl.options[:threaded]) Thread.new do begin @parent.send(action, m, options) @@ -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 @@ -333,7 +384,7 @@ class Bot mul = multi? ? " multi" : " single" opt = optional? ? " optional" : " needed" if @regexp - reg = " regexp=%s index=%d" % [@regexp, @index] + reg = " regexp=%s index=%s" % [@regexp, @index] else reg = nil end @@ -514,12 +565,15 @@ class Bot s = "#{not_needed ? "(?:" : ""}#{whites}(#{sub})#{ not_needed ? ")?" : ""}" } # debug "Replaced dyns: #{rx.inspect}" - rx.gsub!(/((?:\\ )*)\\\[/, "(?:\\1") + rx.gsub!(/((?:\\ )*)((?:\\\[)+)/, '\2\1') + # debug "Corrected optionals spacing: #{rx.inspect}" + rx.gsub!(/\\\[/, "(?:") rx.gsub!(/\\\]/, ")?") # debug "Delimited optionals: #{rx.inspect}" rx.gsub!(/(?:\\ )+/, "\\s+") # debug "Corrected spaces: #{rx.inspect}" - @regexp = Regexp.new("^#{rx}$") + # Created message (such as by fake_message) can contain multiple lines + @regexp = /\A#{rx}\z/m end # Recognize the provided string components, returning a hash of @@ -528,20 +582,19 @@ 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? - - options = {} - 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}" debug "Associating #{debug_match} with dyn items #{@dyn_items.join(', ')}" + options = @defaults.dup + @dyn_items.each_with_index { |it, i| next if i == 0 item = it.name @@ -586,7 +639,7 @@ class Bot } options.delete_if {|k, v| v.nil?} # Remove nil values. - return options, nil + return options end def inspect