################################################################################ # isragent.rb tries to make In Soviet Russia jokes out of input. # # To run: put subbot.org/isragent/isragent-api.yaml in the same directory as # this file. Then: # # C:\isragent>ruby isragent.rb # I have loaded C:/isragent/isragent-api.yaml. # # > In California, you can always find a party. # In Soviet Russia, the party can always find YOU! # # > ################################################################################ require 'yaml' require 'drb' class MyAgent attr_accessor :plurals, :singulars, :verbs, :verb_forms, :names def initialize(args=Array.new) @workingDir = File.expand_path(File.dirname(__FILE__)) @filename = @workingDir + "/isragent-api.yaml" @isragent_state = @workingDir + "/memory/isragent_state.txt" unless load_message = load_synonyms @patterns = [ [/^(.*?) is a synonym for (.*)/, :add_synonym], [/^save syn/, :save_synonyms], [/^load syn/, :load_synonyms], [/^add pattern (.*), (.*)/, :add_pattern], [/^>(.*)/, :defining_method], [/^restart/, :restart] ] save_synonyms # creates patterns.yaml on first use. else puts load_message end @method_def = '' @plurals = Array.new @plurals << "I" @singulars = Array.new @verbs = [ "is", "=", "are", "has", "have", "eat", "eats" ] @verb_forms = { "is" => "are", "has" => "have", "eats" => "eat" } @names = Array.new @save = true @logging = false # not being used; $logger is called from client DRb.start_service() @link = DRbObject.new(nil, 'druby://localhost:9050') end def def_method method_def file = IO.read(__FILE__) return "Doesn't have 'end # class'" unless file =~ /(.*)end # class(.*)if \$0 == __FILE__(.*)/m grp1 = $1.to_s grp2 = $2.to_s grp3 = $3.to_s new_filename = get_new_filename File.open(new_filename, "w+") { |f| f.puts grp1 + method_def + "\n\nend # class\n\nif $0 == __FILE__" + grp3 } @patterns.push( [@new_regexp, @new_symbol] ) save_synonyms MyAgent.class_eval(method_def) end # If this file has a '.' + a number before the '.rb' extension # (i.e. foo.0.rb), increment the number (i.e. return foo.1.rb). # Otherwise return the name of this file unchanged. def get_new_filename old_filename = __FILE__ if old_filename =~ /(.*)\.(\d+)\.rb$/ incr = $2.to_i + 1 return new_filename = $1.to_s + "." + incr.to_s + ".rb" end return old_filename # Dangerous! end # Match input against each regexp pattern in @patterns; # if there's a match, call the method associated with the pattern. def getResponse input r = "Default response." # Strip names from input (don't strip if input is "NAME is not a name") @names.each { |name| escaped_name = Regexp.escape(name) if input =~ /^#{escaped_name}[:, ]*(.*)/ new_input = $1.strip break if new_input =~ /^is not a name/ input = new_input break end } #puts "input==#{input}" @patterns.each { |pattern| if input =~ pattern[0] return [self.send(pattern[1], pattern[0], input), 2] rescue return r end } return r end def load_synonyms(pattern=nil, input=nil) if File.exist?(@filename) @patterns = YAML.load_file(@filename) return "I have loaded #{@filename}." end return false end def save_synonyms(pattern=nil, input=nil) r = "Eep! error saving synonyms! " begin File.open(@filename, 'w') do |out| YAML.dump(@patterns, out) end return "Okay I have saved the synonyms to disk." rescue => error return r << error end end def add_synonym(pattern, input) return "Problem matching pattern." unless input =~ pattern #grp1 = %r{^#{$1}} grp1 = %r{(?i-mx:^#{$1})} #grp2 = %r{^#{$2}} grp2 = %r{(?i-mx:^#{$2})} method ='' @patterns.each { |p| #puts "p[0]==#{p[0]}; p[0].class==#{p[0].class}; grp2==#{grp2}; grp2.class==#{grp2.class}" if p[0].to_s == grp2.to_s method = p[1] break end } return unless method.to_s != nil @patterns.push( [grp1, method] ) save_synonyms return "Okay, #{grp1} has been added, and will now call #{method}." end def add_pattern(pattern=nil, input=nil) if input =~ pattern @new_regexp = $1 @new_symbol = $2 @new_regexp.gsub!(/\//, '') new_re = %r{#{@new_regexp}} # Make the regexp case insensitive. new_re_s = new_re.to_s if new_re_s =~ /(-mix)/ new_re_s = $~.pre_match << "i-mx" << $~.post_match end @new_regexp = %r{#{new_re_s}} #puts "@new_regexp==#{@new_regexp}" method_def = " def #{@new_symbol}(pattern=nil, input=nil)\n return \"#\{input} doesn't match #\{pattern}\" unless input =~ pattern\n return 'Default response from " + @new_symbol + "'\n end # method_def" return def_method(method_def) else return "#{input} doesn't match #{pattern}." end end def defining_method(pattern=nil, input=nil) if input =~ pattern @method_def << "\n " << $1.to_s if @method_def =~ /end # method_def/ return def_method(@method_def) end return '' else return "#{input} doesn't match #{pattern}." end end def method_missing input, *rest return getResponse(input.to_s) end # Strips delimiters. def process str str.strip! str.sub!(/\?$/, '') str.sub!(/^\[/, '') str.sub!(/\]$/, '') str.sub!(/^(?:"|')/, '') str.sub!(/(?:"|')$/, '') str.sub!(/\.$/, '') str.sub!(/;$/, '') str.sub!(/,$/, '') str.sub!(/!$/, '') str.sub!(/\?$/, '') return str end def restart(pattern=nil,input=nil) exec "ruby #{__FILE__}" end # method_def def getPlural(verb) @verb_forms.fetch(verb, verb) end def isPlural?(word = '') @plurals.each { |plural| if word =~ /^#{plural}$/i then return true end } if word =~ /socrates$/i; return false; end if word =~ /men$/i; return true; end @singulars.each { |singular| if word =~ /^#{singular}$/i then return false end } if word =~ /s$/i; return true; end end def add_to_plurals(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1 @plurals.push(grp1) return "Okay I have recorded #{grp1} as a plural form." end # method_def def add_to_singulars(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern grp1 = process $1 @singulars.push(grp1) return "Okay I have recorded #{grp1} as a singular form." end # method_def def begin_log(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern file = process $1 file = @workingDir + "/log.txt" unless file != nil and file != '' @logging = true $logger = File.open(file, "w") t = Time.now; y = t.year; m = t.month; d = t.day; h= t.hour; min = t.min; s = t.sec mins = sprintf("%02d", min) secs = sprintf("%02d", s) time = "#{y}/#{m}/#{d} #{h}:#{mins}:#{secs}" return "Okay I have started logging to #{file} at #{time}" end # method_def def end_log(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern t = Time.now; y = t.year; m = t.month; d = t.day; h= t.hour; min = t.min; s = t.sec mins = sprintf("%02d", min) secs = sprintf("%02d", s) time = "#{y}/#{m}/#{d} #{h}:#{mins}:#{secs}" r = "Okay I have stopped logging at #{time}." $logger.puts(r); $logger.close; $logger == nil @logging = false return r end # method_def def add_name(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern #grp1 = process $1 grp1 = $1.strip grp1.sub!(/^(?:"|')/, '') grp1.sub!(/(?:"|')$/, '') grp1.sub!(/(?:,)$/, '') @names.push(grp1) return "Okay, #{grp1} is a name." end # method_def def what_are_names(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern return @names.join(", ") end # method_def def save(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern begin # NOT WORKING orig_file = process $1 symbol = ("@" + orig_file).to_sym puts "symbol==#{symbol}" puts "self.symbol==#{self.send(symbol).inspect}" orig_file = @workingDir + '/memory/' + orig_file puts "orig_file1==#{orig_file}" File.open(orig_file, 'w') do |out| YAML::dump(symbol, out) end return "Okay, I have saved #{orig_file}." rescue; end if orig_file == "" then orig_file = @workingDir + '/memory/' end puts "orig_file2==#{orig_file}" r = "Eep! error saving gagent_state! " begin file = orig_file + "plurals" File.open(file, 'w') do |out| YAML::dump(@plurals, out) end file = orig_file + "singulars" File.open(file, "w") do |out| YAML::dump(@singulars, out) end file = orig_file + "verbs" File.open(file, "w") do |out| YAML::dump(@verbs, out) end file = orig_file + "verb_forms" File.open(file, "w") do |out| YAML::dump(@verb_forms, out) end file = orig_file + "names" File.open(file, "w") do |out| YAML::dump(@names, out) end return "Okay I have saved my state to disk." rescue => error return r << error end end # method_def def load(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern begin orig_file = process $1 symbol = orig_file.to_sym file = @workingDir + '/memory/' + orig_file File.open(file, 'r') do |out| symbol = YAML::load_file(file) end return "Okay I have loaded #{file}." rescue; end if orig_file == "" then orig_file = @workingDir + '/memory/' end #puts "orig_file==#{orig_file}" r = "Eep! error loading isragent_state! " begin file = orig_file + "plurals" File.open(file, 'r') do |out| @plurals = YAML::load_file(file) end file = orig_file + "singulars" File.open(file, "r") do |out| @singulars = YAML::load_file(file) end file = orig_file + "verbs" File.open(file, "r") do |out| @verbs = YAML::load_file(file) end file = orig_file + "verb_forms" File.open(file, "r") do |out| @verb_forms = YAML::load_file(file) end file = orig_file + "names" File.open(file, "r") do |out| @names = YAML::load_file(file) end return "Okay I have loaded the previously stored state." rescue => error return r << error end end # method_def def set_save_mode_off(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern @save = false return "Okay I have turned off save mode." end # method_def def set_save_mode_on(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern @save = true return "Okay I have turned on save mode." end # method_def def x_is_not_a_name(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern name_to_del = $1 result = @names.delete(name_to_del) if result == nil return "I can't find #{name_to_del} among the names I have stored." end return "Okay, I have deleted #{result}." end # method_def def is_x_a_name(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern name = $1 if @names.include?(name) return "Yes, #{name} is a name." end return "No, #{name} is not a name." end # method_def alias is_x_a_name? is_x_a_name def what_is_save_mode(pattern=nil, input=nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern save_mode = "on"; save_mode = "off" unless @save return "The save mode is #{save_mode}." end # method_def def make_joke(pattern = nil, input = nil) return "#{input} doesn't match #{pattern}" unless input =~ pattern #puts "make_joke: #{pattern}, #{input}" #puts "$1==#{$1}" sentence = process($1) begin subject = @link.send("subject: #{sentence}") rescue subject = "" end begin verb = @link.send("verb: #{sentence}") rescue verb = "" end begin object = @link.send("object: #{sentence}") rescue object = "" end puts "subject=#{subject}; verb=#{verb}; object=#{object}" if isPlural?(object) then pl_verb = getPlural(verb) end if pl_verb != nil then verb = pl_verb end if pl_verb == nil then verb = verb + 's' unless @verb_forms.include?(verb) or verb =~ / / end new_subject = make_object_subject(object) return "In Soviet Russia, #{new_subject} #{verb} YOU!" end # method_def def make_object_subject(phrase= '') if phrase =~ /^(a|an) / then phrase.sub!(/^(a|an) /, 'the ') end return phrase end # method_def end # class if $0 == __FILE__ bot = MyAgent.new print "\n> "; $stdout.flush while (line = gets) !~ /^:q/ and line !~ /^$/ and line !~ /^quit/ and line !~ /^exit/ and line !~ /^bye$/i begin; $logger.puts("> #{line}"); rescue; end response = bot.send(line.to_s).to_s if response.class == "Array" then response = response[0] end response = response.chop # remove the score puts response + "\n\n" begin; $logger.puts(response); rescue; end print "> "; $stdout.flush end puts "Bye" end # of file