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