X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=data%2Frbot%2Fplugins%2Fmarkov.rb;h=deeb9cae4cb3bdbdf0b67d306b7a9729bbefa3c6;hb=57f03b3cd65ff75684e929553d8a3d64713afd28;hp=089d939decc4ccd32e42236a5d02ee1d5c41b969;hpb=041ceb742df2076c93e3fb88fe7a06b5e9c79810;p=user%2Fhenk%2Fcode%2Fruby%2Frbot.git diff --git a/data/rbot/plugins/markov.rb b/data/rbot/plugins/markov.rb old mode 100644 new mode 100755 index 089d939d..deeb9cae --- a/data/rbot/plugins/markov.rb +++ b/data/rbot/plugins/markov.rb @@ -44,7 +44,7 @@ class MarkovPlugin < Plugin @learning_queue = Queue.new @learning_thread = Thread.new do while s = @learning_queue.pop - learn s + learn_line s sleep 0.5 end end @@ -60,30 +60,85 @@ class MarkovPlugin < Plugin def generate_string(word1, word2) # limit to max of markov.max_words words - output = word1 + " " + word2 + if word2 + output = "#{word1} #{word2}" + else + output = word1.to_s + end - # try to avoid :nonword in the first iteration - wordlist = @registry["#{word1} #{word2}"] - wordlist.delete(:nonword) - if not wordlist.empty? - word3 = wordlist[rand(wordlist.length)] - output = output + " " + word3 - word1, word2 = word2, word3 + if @registry.key? output + wordlist = @registry[output] + wordlist.delete(:nonword) + else + output.downcase! + keys = [] + @registry.each_key(output) do |key| + if key.downcase.include? output + keys << key + else + break + end + end + if keys.empty? + keys = @registry.keys.select { |k| k.downcase.include? output } + end + return nil if keys.empty? + while key = keys.delete_one + wordlist = @registry[key] + wordlist.delete(:nonword) + unless wordlist.empty? + output = key + word1, word2 = output.split + break + end + end end + return nil if wordlist.empty? + + word3 = wordlist.pick_one + output << " #{word3}" + word1, word2 = word2, word3 (@bot.config['markov.max_words'] - 1).times do wordlist = @registry["#{word1} #{word2}"] break if wordlist.empty? - word3 = wordlist[rand(wordlist.length)] + word3 = wordlist.pick_one break if word3 == :nonword - output = output + " " + word3 + output << " #{word3}" word1, word2 = word2, word3 end return output end def help(plugin, topic="") - "markov plugin: listens to chat to build a markov chain, with which it can (perhaps) attempt to (inanely) contribute to 'discussion'. Sort of.. Will get a *lot* better after listening to a lot of chat. usage: 'markov' to attempt to say something relevant to the last line of chat, if it can. other options to markov: 'ignore' => ignore a hostmask (accept no input), 'status' => show current status, 'probability []' => set the % chance of rbot responding to input, or display the current probability, 'chat' => try and say something intelligent, 'chat about ' => riff on a word pair (if possible)" + topic, subtopic = topic.split + + case topic + when "ignore" + case subtopic + when "add" + "markov ignore add => ignore a hostmask or a channel" + when "list" + "markov ignore list => show ignored hostmasks and channels" + when "remove" + "markov ignore remove => unignore a hostmask or channel" + else + "ignore hostmasks or channels -- topics: add, remove, list" + end + when "status" + "markov status => show if markov is enabled, probability and amount of messages in queue for learning" + when "probability" + "markov probability [] => set the % chance of rbot responding to input, or display the current probability" + when "chat" + case subtopic + when "about" + "markov chat about [] => talk about or riff on a word pair (if possible)" + else + "markov chat => try to say something intelligent" + end + else + "markov plugin: listens to chat to build a markov chain, with which it can (perhaps) attempt to (inanely) contribute to 'discussion'. Sort of.. Will get a *lot* better after listening to a lot of chat. Usage: 'chat' to attempt to say something relevant to the last line of chat, if it can -- help topics: ignore, status, probability, chat, chat about" + end end def clean_str(s) @@ -127,25 +182,25 @@ class MarkovPlugin < Plugin s = @bot.config['markov.ignore'] s.delete user @bot.config['ignore'] = s - m.reply "#{user} removed" + m.reply _("%{u} removed") % { :u => user } else - m.reply "not found in list" + m.reply _("not found in list") end when 'add': if user if @bot.config['markov.ignore'].include?(user) - m.reply "#{user} already in list" + m.reply _("%{u} already in list") % { :u => user } else @bot.config['markov.ignore'] = @bot.config['markov.ignore'].push user - m.reply "#{user} added to markov ignore list" + m.reply _("%{u} added to markov ignore list") % { :u => user } end else - m.reply "give the name of a person or channel to ignore" + m.reply _("give the name of a person or channel to ignore") end when 'list': - m.reply "I'm ignoring #{@bot.config['markov.ignore'].join(", ")}" + m.reply _("I'm ignoring %{ignored}") % { :ignored => @bot.config['markov.ignore'].join(", ") } else - m.reply "have markov ignore the input from a hostmask or a channel. usage: markov ignore add ; markov ignore remove ; markov ignore list" + m.reply _("have markov ignore the input from a hostmask or a channel. usage: markov ignore add ; markov ignore remove ; markov ignore list") end end @@ -196,10 +251,10 @@ class MarkovPlugin < Plugin def chat(m, params) line = generate_string(params[:seed1], params[:seed2]) - if line != "#{params[:seed1]} #{params[:seed2]}" - m.reply line + if line and line != [params[:seed1], params[:seed2]].compact.join(" ") + m.reply line else - m.reply "I can't :(" + m.reply _("I can't :(") end end @@ -218,11 +273,15 @@ class MarkovPlugin < Plugin if output.length > 1 m.reply output.join(" ") else - m.reply "I can't :(" + m.reply _("I can't :(") end end - - def message(m) + + def learn(*lines) + lines.each { |l| @learning_queue.push l } + end + + def unreplied(m) return if ignore? m # in channel message, the kind we are interested in @@ -231,12 +290,12 @@ class MarkovPlugin < Plugin if m.action? message = "#{m.sourcenick} #{message}" end - - @learning_queue.push message + + learn message random_markov(m, message) unless m.replied? end - def learn(message) + def learn_line(message) # debug "learning #{message}" wordlist = message.split(/\s+/) return unless wordlist.length >= 2 @@ -249,6 +308,71 @@ class MarkovPlugin < Plugin k = "#{word1} #{word2}" @registry[k] = @registry[k].push(:nonword) end + + # TODO allow learning from URLs + def learn_from(m, params) + begin + path = params[:file] + file = File.open(path, "r") + pattern = params[:pattern].empty? ? nil : Regexp.new(params[:pattern].to_s) + rescue Errno::ENOENT + m.reply _("no such file") + return + end + + if file.eof? + m.reply _("the file is empty!") + return + end + + if params[:testing] + lines = [] + range = case params[:lines] + when /^\d+\.\.\d+$/ + Range.new(*params[:lines].split("..").map { |e| e.to_i }) + when /^\d+$/ + Range.new(1, params[:lines].to_i) + else + Range.new(1, [@bot.config['send.max_lines'], 3].max) + end + + file.each do |line| + next unless file.lineno >= range.begin + lines << line.chomp + break if file.lineno == range.end + end + + lines = lines.map do |l| + pattern ? l.scan(pattern).to_s : l + end.reject { |e| e.empty? } + + if pattern + unless lines.empty? + m.reply _("example matches for that pattern at lines %{range} include: %{lines}") % { + :lines => lines.map { |e| Underline+e+Underline }.join(", "), + :range => range.to_s + } + else + m.reply _("the pattern doesn't match anything at lines %{range}") % { + :range => range.to_s + } + end + else + m.reply _("learning from the file without a pattern would learn, for example: ") + lines.each { |l| m.reply l } + end + + return + end + + if pattern + file.each { |l| learn(l.scan(pattern).to_s) } + else + file.each { |l| learn(l.chomp) } + end + + m.okay + end end plugin = MarkovPlugin.new @@ -258,11 +382,16 @@ plugin.map 'markov ignore', :action => "ignore" plugin.map 'markov enable', :action => "enable" plugin.map 'markov disable', :action => "disable" plugin.map 'markov status', :action => "status" -plugin.map 'chat about :seed1 :seed2', :action => "chat" +plugin.map 'chat about :seed1 [:seed2]', :action => "chat" plugin.map 'chat', :action => "rand_chat" plugin.map 'markov probability [:probability]', :action => "probability", :requirements => {:probability => /^\d+%?$/} +plugin.map 'markov learn from :file [:testing [:lines lines]] [using pattern *pattern]', :action => "learn_from", :thread => true, + :requirements => { + :testing => /^testing$/, + :lines => /^(?:\d+\.\.\d+|\d+)$/ } plugin.default_auth('ignore', false) plugin.default_auth('probability', false) +plugin.default_auth('learn', false)