| Class | Irc::Bot::MessageTemplate |
| In: |
lib/rbot/messagemapper.rb
|
| Parent: | Object |
MessageTemplate is the class that holds the actual message template map()’d by a BotModule and handled by a MessageMapper
| botmodule | [R] | |
| defaults | [R] | |
| items | [R] | |
| options | [R] | |
| regexp | [R] | |
| template | [R] |
Create a new MessageTemplate associated to BotModule botmodule, with template template and options opts
# File lib/rbot/messagemapper.rb, line 411
411: def initialize(botmodule, template, hash={})
412: raise ArgumentError, "Third argument must be a hash!" unless hash.kind_of?(Hash)
413: @defaults = hash[:defaults].kind_of?(Hash) ? hash.delete(:defaults) : {}
414: @requirements = hash[:requirements].kind_of?(Hash) ? hash.delete(:requirements) : {}
415: @template = template
416: case botmodule
417: when String
418: @botmodule = botmodule
419: when Plugins::BotModule
420: @botmodule = botmodule.name
421: else
422: raise ArgumentError, "#{botmodule.inspect} is not a botmodule nor a botmodule name"
423: end
424:
425: self.items = template
426: # @dyn_items is an array of MessageParameters, except for the first entry
427: # which is the template
428: @dyn_items = @items.collect { |it|
429: if it.kind_of?(Symbol)
430: i = it.to_s
431: opt = MessageParameter.new(i)
432: if i.sub!(/^\*/,"")
433: opt.name = i
434: opt.multi = true
435: end
436: opt.default = @defaults[opt.name]
437: opt.collector = @requirements[opt.name]
438: opt
439: else
440: nil
441: end
442: }
443: @dyn_items.unshift(template).compact!
444: debug "Items: #{@items.inspect}; dyn items: #{@dyn_items.inspect}"
445:
446: self.regexp = template
447: debug "Command #{template.inspect} in #{@botmodule} will match using #{@regexp}"
448:
449: set_auth_path(hash)
450:
451: unless hash.has_key?(:action)
452: hash[:action] = items[0]
453: end
454:
455: @options = hash
456:
457: # debug "Create template #{self.inspect}"
458: end
# File lib/rbot/messagemapper.rb, line 645
645: def inspectinspect
646: when_str = @requirements.empty? ? "" : " when #{@requirements.inspect}"
647: default_str = @defaults.empty? ? "" : " || #{@defaults.inspect}"
648: "<#{self.class.to_s} #{@items.map { |c| c.inspect }.join(' ').inspect}#{default_str}#{when_str}>"
649: end
# File lib/rbot/messagemapper.rb, line 499
499: def items=(str)
500: raise ArgumentError, "template #{str.inspect} should be a String" unless str.kind_of?(String)
501:
502: # split and convert ':xyz' to symbols
503: items = str.strip.split(/\]?\s+\[?|\]?$/).collect { |c|
504: # there might be extra (non-alphanumeric) stuff (e.g. punctuation) after the symbol name
505: if /^(:|\*)(\w+)(.*)/ =~ c
506: sym = ($1 == ':' ) ? $2.intern : "*#{$2}".intern
507: if $3.empty?
508: sym
509: else
510: [sym, $3]
511: end
512: else
513: c
514: end
515: }.flatten
516: @items = items
517:
518: raise ArgumentError, "Illegal template -- first component cannot be dynamic: #{str.inspect}" if @items.first.kind_of? Symbol
519:
520: raise ArgumentError, "Illegal template -- first component cannot be optional: #{str.inspect}" if @items.first =~ /\[|\]/
521:
522: # Verify uniqueness of each component.
523: @items.inject({}) do |seen, item|
524: if item.kind_of? Symbol
525: # We must remove the initial * when present,
526: # because the parameters hash will intern both :item and *item as :item
527: it = item.to_s.sub(/^\*/,"").intern
528: raise ArgumentError, "Illegal template -- duplicate item #{it} in #{str.inspect}" if seen.key? it
529: seen[it] = true
530: end
531: seen
532: end
533: end
Recognize the provided string components, returning a hash of recognized values, or [nil, reason] if the string isn‘t recognized.
# File lib/rbot/messagemapper.rb, line 581
581: def recognize(m)
582:
583: debug "Testing #{m.message.inspect} against #{self.inspect}"
584:
585: matching = @regexp.match(m.message)
586: return MessageMapper::NoMatchFailure.new(self, m) unless matching
587: return MessageMapper::PartialMatchFailure.new(self, m) unless matching[0] == m.message
588:
589: return MessageMapper::NotPrivateFailure.new(self, m) if @options.has_key?(:private) && !@options[:private] && m.private?
590: return MessageMapper::NotPublicFailure.new(self, m) if @options.has_key?(:public) && !@options[:public] && !m.private?
591:
592: debug_match = matching[1..-1].collect{ |d| d.inspect}.join(', ')
593: debug "#{m.message.inspect} matched #{@regexp} with #{debug_match}"
594: debug "Associating #{debug_match} with dyn items #{@dyn_items.join(', ')}"
595:
596: options = @defaults.dup
597:
598: @dyn_items.each_with_index { |it, i|
599: next if i == 0
600: item = it.name
601: debug "dyn item #{item} (multi-word: #{it.multi?.inspect})"
602: if it.multi?
603: if matching[i].nil?
604: default = it.default
605: case default
606: when Array
607: value = default.clone
608: when String
609: value = default.strip.split
610: when nil, false, []
611: value = []
612: else
613: warning "Unmanageable default #{default} detected for :*#{item.to_s}, using []"
614: value = []
615: end
616: case default
617: when String
618: value.instance_variable_set(:@string_value, default)
619: else
620: value.instance_variable_set(:@string_value, value.join(' '))
621: end
622: else
623: value = matching[i].split
624: value.instance_variable_set(:@string_value, matching[i])
625: end
626: def value.to_s
627: @string_value
628: end
629: else
630: if matching[i].nil?
631: warning "No default value for option #{item.inspect} specified" unless @defaults.has_key?(item)
632: value = it.default
633: else
634: value = it.collect(matching[i])
635: end
636: end
637: options[item] = value
638: debug "set #{item} to #{options[item].inspect}"
639: }
640:
641: options.delete_if {|k, v| v.nil?} # Remove nil values.
642: return options
643: end
# File lib/rbot/messagemapper.rb, line 535
535: def regexp=(str)
536: # debug "Original string: #{str.inspect}"
537: rx = Regexp.escape(str)
538: # debug "Escaped: #{rx.inspect}"
539: rx.gsub!(/((?:\\ )*)(:|\\\*)(\w+)/) { |m|
540: whites = $1
541: is_single = $2 == ":"
542: name = $3.intern
543:
544: not_needed = @defaults.has_key?(name)
545:
546: has_req = @requirements[name]
547: debug "Requirements for #{name}: #{has_req.inspect}"
548: case has_req
549: when nil
550: sub = is_single ? "\\S+" : ".*?"
551: when Regexp
552: # Remove captures and the ^ and $ that are sometimes placed in requirement regexps
553: sub = has_req.mm_cleanup
554: when String
555: sub = Regexp.escape(has_req)
556: when Array
557: sub = has_req[0].mm_cleanup
558: when Hash
559: sub = has_req[:regexp].mm_cleanup
560: else
561: warning "Odd requirement #{has_req.inspect} of class #{has_req.class} for parameter '#{name}'"
562: sub = Regexp.escape(has_req.to_s) rescue "\\S+"
563: end
564: debug "Regexp for #{name}: #{sub.inspect}"
565: s = "#{not_needed ? "(?:" : ""}#{whites}(#{sub})#{ not_needed ? ")?" : ""}"
566: }
567: # debug "Replaced dyns: #{rx.inspect}"
568: rx.gsub!(/((?:\\ )*)((?:\\\[)+)/, '\2\1')
569: # debug "Corrected optionals spacing: #{rx.inspect}"
570: rx.gsub!(/\\\[/, "(?:")
571: rx.gsub!(/\\\]/, ")?")
572: # debug "Delimited optionals: #{rx.inspect}"
573: rx.gsub!(/(?:\\ )+/, "\\s+")
574: # debug "Corrected spaces: #{rx.inspect}"
575: # Created message (such as by fake_message) can contain multiple lines
576: @regexp = /\A#{rx}\z/m
577: end
# File lib/rbot/messagemapper.rb, line 651
651: def requirements_for(name)
652: name = name.to_s.sub(/^\*/,"").intern if (/^\*/ =~ name.inspect)
653: presence = (@defaults.key?(name) && @defaults[name].nil?)
654: requirement = case @requirements[name]
655: when nil then nil
656: when Regexp then "match #{@requirements[name].inspect}"
657: else "be equal to #{@requirements[name].inspect}"
658: end
659: if presence && requirement then "#{name} must be present and #{requirement}"
660: elsif presence || requirement then "#{name} must #{requirement || 'be present'}"
661: else "#{name} has no requirements"
662: end
663: end
# File lib/rbot/messagemapper.rb, line 460
460: def set_auth_path(hash)
461: if hash.has_key?(:auth)
462: warning "Command #{@template.inspect} in #{@botmodule} uses old :auth syntax, please upgrade"
463: end
464: if hash.has_key?(:full_auth_path)
465: warning "Command #{@template.inspect} in #{@botmodule} sets :full_auth_path, please don't do this"
466: else
467: pre = @botmodule
468: words = items.reject{ |x|
469: x == pre || x.kind_of?(Symbol) || x =~ /\[|\]/
470: }
471: if words.empty?
472: post = nil
473: else
474: post = words.first
475: end
476: if hash.has_key?(:auth_path)
477: extra = hash[:auth_path]
478: if extra.sub!(/^:/, "")
479: pre += "::" + post
480: post = nil
481: end
482: if extra.sub!(/:$/, "")
483: if words.length > 1
484: post = [post,words[1]].compact.join("::")
485: end
486: end
487: pre = nil if extra.sub!(/^!/, "")
488: post = nil if extra.sub!(/!$/, "")
489: extra = nil if extra.empty?
490: else
491: extra = nil
492: end
493: hash[:full_auth_path] = [pre,extra,post].compact.join("::")
494: debug "Command #{@template} in #{botmodule} will use authPath #{hash[:full_auth_path]}"
495: # TODO check if the full_auth_path is sane
496: end
497: end