X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=lib%2Frbot%2Fmessagemapper.rb;h=3966bc17f7360ec3853cda6995c479d3ab9e7590;hb=a19f7bfb97e5f36e6b282fcc0982584838e86a0a;hp=c1691aff6a8fc2e7989e1cf15217224c780a7a1a;hpb=8ddc8ed81f1f5065705c68d2abda5804319ff981;p=user%2Fhenk%2Fcode%2Fruby%2Frbot.git diff --git a/lib/rbot/messagemapper.rb b/lib/rbot/messagemapper.rb index c1691aff..3966bc17 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 @@ -148,12 +199,12 @@ class Bot # # Further examples: # - # # match 'karmastats' and call my stats() method - # plugin.map 'karmastats', :action => 'stats' - # # match 'karma' with an optional 'key' and call my karma() method - # plugin.map 'karma :key', :defaults => {:key => false} - # # match 'karma for something' and call my karma() method - # plugin.map 'karma for :key' + # # match 'pointstats' and call my stats() method + # plugin.map 'pointstats', :action => 'stats' + # # match 'points' with an optional 'key' and call my points() method + # plugin.map 'points :key', :defaults => {:key => false} + # # match 'points for something' and call my points() method + # plugin.map 'points for :key' # # # two matches, one for public messages in a channel, one for # # private messages which therefore require a channel argument @@ -194,30 +245,40 @@ class Bot return false if @templates.empty? failures = [] @templates.each do |tmpl| - options, failure = tmpl.recognize(m) - if options.nil? - failures << [tmpl, failure] + params = tmpl.recognize(m) + if params.kind_of? Failure + failures << params 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.in_thread && (tmpl.options[:thread] || tmpl.options[:threaded]) + debug "template match found and auth'd: #{action.inspect} #{params.inspect}" + if !m.in_thread and (tmpl.options[:thread] or tmpl.options[:threaded]) and + (defined? WebServiceUser and not m.source.instance_of? WebServiceUser) + # Web service: requests are handled threaded anyway and we want to + # wait for the responses. + + # since the message action is in a separate thread, the message may be + # delegated to unreplied() before the thread has a chance to actually + # mark it as replied. since threading is used mostly for commands that + # are known to take some processing time (e.g. download a web page) before + # giving an output, we just mark these as 'replied' here + m.replied = true Thread.new do begin - @parent.send(action, m, options) + @parent.send(action, m, params) rescue Exception => e error "In threaded action: #{e.message}" debug e.backtrace.join("\n") end end else - @parent.send(action, m, options) + @parent.send(action, m, params) end return true @@ -228,13 +289,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 +394,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 @@ -531,13 +592,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 +649,7 @@ class Bot } options.delete_if {|k, v| v.nil?} # Remove nil values. - return options, nil + return options end def inspect