# Wrapper for LogicAgent, adding user commands to modify the score, etc. # This class can function both as a command-line program and # as an IRC bot when it is loaded by the RedTurnip IRC bot framework. begin require 'logicagent' # for when we start from the command line rescue LoadError begin require 'mod/bot/logicagent' # for when we start from RedTurnip rescue LoadError require_relative 'logicagent' end end BasicSocket.do_not_reverse_lookup = true class LogicBot def initialize print "Hello\n" @agent = MyAgent.new @THRESHHOLD = 1 #@SCORE_MOD = 0 @SCORE_MOD = 1 # 2008-04-29: try raising the default score... @MAX_RESPONSE = 10000 @MY_NAME = "(?i)(?:logicagentbot|logicbot|logicagent|logbot|logician)" @DEBUG = 0 @agent.DEBUG = @DEBUG @rules = [] @rules.push('if input =~ /\byour\b/i and response =~ /\byour\b/i then response.gsub!(/\byour\b/i, "my") end') @rules.push('if input =~ /\byour\b/i and response =~ /\bme\b/ then response.gsub!(/\bme\b/, "you") end') @rules.push('if input =~ /\bI\b/i and response =~ /\bI\b/i then response.gsub!(/\bI\b/i, "you") end') @rules.push('if input =~ /\bI\b/i and response =~ /\byour\b/i then response.gsub!(/\byour\b/i, "my") end') @rules.push("if input =~ /(.*) is who\\?$/ then response = self.send(\"who is \#{$1}\") end") @rules.push("if input =~ /\\bmy\\b/ then response.gsub!(/\\bmy\\b/, 'your') end") @rules.push("if input =~ /^when is (.*)/ and score <= 3 then response = self.send(\"what is \#{$1}\")[0]; score += 3 end") @rules.push("if input =~ /^([a-z])=([a-z])$/ then response = self.send(\"\#{$1} = \#{$2}\") end") @rules.push("if input =~ /^([a-z])!=([a-z])$/ then response = self.send(\"\#{$1} != \#{$2}\") end") @rules.push("if input =~ /^([a-z])=([a-z])\\?$/ then response = self.send\(\"does \#{$1} = \#{$2}?\"\) end") @SAFE_LEVEL = "low" @Low_safe_level_words = ["low", "default", "0", "off"] @High_safe_level_words = ["high", "on", "1"] end def getResponse(input) input = input.to_s #print 'INPUT==' + input + "\n" # Set threshhold. if input =~ /^what is your name?/ return @MY_NAME.to_s end if input =~ /^#{@MY_NAME}[:,!]* (quiet|be quiet|stfu|shut.*up|soft)/ @THRESHHOLD = @THRESHHOLD + 1 @SCORE_MOD -= 1 return "My threshhold is now " << @THRESHHOLD.to_s << ", and my score_mod is #{@SCORE_MOD}." end if input =~ /^#{@MY_NAME}[:,!]* (speak.*up|loud|louder|i can't hear you)/ @THRESHHOLD = @THRESHHOLD - 1 @SCORE_MOD += 1 return "My threshhold is now " << @THRESHHOLD.to_s << ", and my score_mod is #{@SCORE_MOD}." end # Return threshhold if input =~ /^#{@MY_NAME}[:,!]* what is your (threshhold|threshold|volume)/ or input =~ /^#{@MY_NAME}[:,!]* (?:threshold|threshhold|volume)/ return "My threshhold is currently " << @THRESHHOLD.to_s end # Provide help message. if input =~ /^#{@MY_NAME}[:,!]* help/ return "I make logical inferences using the Aristotelian syllogism." end # trap restart case input when /^#{@MY_NAME}[:,!]* restart/, /^restart/ return "That command is disabled." # for safety end # add new if-then rules to post-process responses case input when /^#{@MY_NAME}[:,!]* (if .*? =~ .*? then .*)$/ return add_if_rule($1) end # show the rules case input when /^#{@MY_NAME}[:,!]*.* show.* rules/ return show_rules[0] end # delete all rules case input when /^#{@MY_NAME}[:,!]*.* delete.* rules/ return delete_rules end # delete a rule case input when /^#{@MY_NAME}[:,!]*.* delete.* rule.* (\d+)/ return delete_rule($1.to_i) end # read in rules from a file case input when /^#{@MY_NAME}[:,!]*.* read.* rules.* (.*)/, /^#{@MY_NAME}[:,!]*.* load.* rules.* (.*)/ return read_rules($1.to_s) end # set safe level case input when /^#{@MY_NAME}[:,! ]*(?:set )?.*safe(?:ty)? level (?:to )?(.*)/ return set_safe_level($1) end # get safe level case input when /^#{@MY_NAME}[:,! ]*what is.*?safe(?:ty)? level/ return get_safe_level end # Set DEBUG level case input when /^#{@MY_NAME}[:,!]*.*on ?(?:the )?debug(?: mode)?/i, /^#{@MY_NAME}[:,!]*.*debug(?: mode)? on/i @DEBUG = 1; @agent.DEBUG = 1; return "I have turned on debug output." when /^#{@MY_NAME}[:,!]*.*(?:off )?(?:the )?debug(?: mode)?(?: off)?/i @DEBUG = 0; @agent.DEBUG = 0; return "I have turned debug output off." end # Hard code in a substitution of "having nothing" for "nothing" # TODO: this is a hack! if input !~ /(.*) having nothing.?$/ and input =~ /(.*) nothing.?$/ input = "#{$1} having nothing" puts "input after NOTHING substitution: #{input}" unless @DEBUG == 0 end # Get the agent's response. if input =~ /^#{@MY_NAME}[:,! ]*(.*)/ then input = $1 end r, s = @agent.getResponse(input) if s == nil then s = 1 end #begin; s = @agent.getScore; rescue NoMethodError; s = 1; end require 'timeout' begin Timeout.timeout(5) { # Timeout after 5 seconds r, s = apply_if_rules(r,s,input) } rescue Exception => e; puts e unless @DEBUG == 0; end s += @SCORE_MOD if r.to_s.size > @MAX_RESPONSE r = r[0..@MAX_RESPONSE] end if r != 'quit' #puts "\n[logicbot] score==#{s}; threshhold==#{@THRESHHOLD}" end if s > @THRESHHOLD if $0 == __FILE__ then return postprocess(r) end return [postprocess(r), s] else print 'r==' + postprocess(r) end return "" end def postprocess input r = input return r end # Respond to natural language method calls. def method_missing input return getResponse(input.to_s) end # add an if-then rule def add_if_rule(rule = '') if rule == '' then return "No rule found." end if @SAFE_LEVEL =~ /#{@Low_safe_level_words.join('|')}$/ @rules.push(rule) return "Okay I have added #{rule}." end return "I can't add any if rules because my safe level is #{@SAFE_LEVEL}." end # apply if-then rules def apply_if_rules(response, score, input) @rules.each { |rule| puts "rule==#{rule}; response==#{response}; score==#{score}" unless @DEBUG == 0 begin eval(rule) rescue Exception => e; puts "Can't apply rule: #{rule}; #{e}" unless @DEBUG == 0; next; end } #puts "returning: response==#{response}; score==#{score}" return [response, score] end # return the rules def show_rules return [@rules.join("\n"), 4] end # delete a rule by its index def delete_rule(i) if i < 1 or i > @rules.size return "The rules go from 1 to #{@rules.size}." end rule = @rules[i-1] @rules.delete_at(i-1) return "Okay, \"#{rule}\" deleted." end # delete all rules def delete_rules @rules = [] return "Okay I have deleted all the rules." end # read rules from a file def read_rules(file) count = 0 IO.foreach(file) { |rule| if rule =~ /^#/ then next end if rule.strip == '' then next end count += 1 puts "#{count}: " + rule unless @DEBUG == 0 self.send(rule) } plural = 's'; if count == 1 then plural = ''; end return "Okay, #{file} read; #{count} rule#{plural}." end # method_def def set_safe_level(level) case level when /\b#{@Low_safe_level_words.join('|')}\b/ @SAFE_LEVEL = "off" return "Okay I have turned off the safe level." when /\b#{@High_safe_level_words.join('|')}\b/ @SAFE_LEVEL = "on" return "Okay I have turned the safe level on." end return "I don't understand #{level}." end def get_safe_level return "My safe level is set to #{@SAFE_LEVEL}." end # method_def end if $0 == __FILE__ b = LogicBot.new print("\n> "); $stdout.flush quitwords = [":q", "quit", "exit", "bye"] while (line = gets)# !~ /^#{quitwords.join('|')}$/ and line !~ /^$/ response = b.send(line); if response.to_s.strip == 'quit' then break end #print "\n" + response.to_s + "\n" print response.to_s + "\n" print("\n> "); $stdout.flush end print "Bye!" end