Guest User

Untitled

a guest
Feb 20th, 2018
286
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Ruby 8.32 KB | None | 0 0
  1. #-- vim:sw=2:et
  2. #++
  3. #
  4. # :title: Markov plugin
  5. #
  6. # Author:: Tom Gilbert <tom@linuxbrit.co.uk>
  7. # Copyright:: (C) 2005 Tom Gilbert
  8. #
  9. # Contribute to chat with random phrases built from word sequences learned
  10. # by listening to chat
  11.  
  12. class MarkovPlugin < Plugin
  13.   Config.register Config::BooleanValue.new('markov.enabled',
  14.     :default => false,
  15.     :desc => "Enable and disable the plugin")
  16.   Config.register Config::IntegerValue.new('markov.probability',
  17.     :default => 25,
  18.     :validate => Proc.new { |v| (0..100).include? v },
  19.     :desc => "Percentage chance of markov plugin chipping in")
  20.   Config.register Config::ArrayValue.new('markov.ignore',
  21.     :default => [],
  22.     :desc => "Hostmasks and channel names markov should NOT learn from (e.g. idiot*!*@*, #privchan).")
  23.   Config.register Config::IntegerValue.new('markov.max_words',
  24.     :default => 50,
  25.     :validate => Proc.new { |v| (0..100).include? v },
  26.     :desc => "Maximum number of words the bot should put in a sentence")
  27.  
  28.   def initialize
  29.     super
  30.     @registry.set_default([])
  31.     if @registry.has_key?('enabled')
  32.       @bot.config['markov.enabled'] = @registry['enabled']
  33.       @registry.delete('enabled')
  34.     end
  35.     if @registry.has_key?('probability')
  36.       @bot.config['markov.probability'] = @registry['probability']
  37.       @registry.delete('probability')
  38.     end
  39.     if @bot.config['markov.ignore_users']
  40.       debug "moving markov.ignore_users to markov.ignore"
  41.       @bot.config['markov.ignore'] = @bot.config['markov.ignore_users'].dup
  42.       @bot.config.delete('markov.ignore_users'.to_sym)
  43.     end
  44.     @learning_queue = Queue.new
  45.     @learning_thread = Thread.new do
  46.       while s = @learning_queue.pop
  47.         learn s
  48.         sleep 0.5
  49.       end
  50.     end
  51.     @learning_thread.priority = -1
  52.   end
  53.  
  54.   def cleanup
  55.     debug 'closing learning thread'
  56.     @learning_queue.push nil
  57.     @learning_thread.join
  58.     debug 'learning thread closed'
  59.   end
  60.  
  61.   def generate_string(word1, word2)
  62.     # limit to max of markov.max_words words
  63.     output = word1 + " " + word2
  64.  
  65.     # try to avoid :nonword in the first iteration
  66.     wordlist = @registry["#{word1} #{word2}"]
  67.     wordlist.delete(:nonword)
  68.     if not wordlist.empty?
  69.       word3 = wordlist[rand(wordlist.length)]
  70.       output = output + " " + word3
  71.       word1, word2 = word2, word3
  72.     end
  73.  
  74.     (@bot.config['markov.max_words'] - 1).times do
  75.       wordlist = @registry["#{word1} #{word2}"]
  76.       break if wordlist.empty?
  77.       word3 = wordlist[rand(wordlist.length)]
  78.       break if word3 == :nonword
  79.       output = output + " " + word3
  80.       word1, word2 = word2, word3
  81.     end
  82.     return output
  83.   end
  84.  
  85.   def help(plugin, topic="")
  86.     "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 [<chance>]' => set the % chance of rbot responding to input, or display the current probability, 'chat' => try and say something intelligent, 'chat about <foo> <bar>' => riff on a word pair (if possible)"
  87.   end
  88.  
  89.   def clean_str(s)
  90.     str = s.dup
  91.     str.gsub!(/^\S+[:,;]/, "")
  92.     str.gsub!(/\s{2,}/, ' ') # fix for two or more spaces
  93.     return str.strip
  94.   end
  95.  
  96.   def probability?
  97.     return @bot.config['markov.probability']
  98.   end
  99.  
  100.   def status(m,params)
  101.     if @bot.config['markov.enabled']
  102.       m.reply "markov is currently enabled, #{probability?}% chance of chipping in, #{@learning_queue.length} messages in queue"
  103.     else
  104.       m.reply "markov is currently disabled"
  105.     end
  106.   end
  107.  
  108.   def ignore?(m=nil)
  109.     return false unless m
  110.     return true if m.address? or m.private?
  111.     @bot.config['markov.ignore'].each do |mask|
  112.       return true if m.channel.downcase == mask.downcase
  113.       return true if m.source.matches?(mask)
  114.     end
  115.     return false
  116.   end
  117.  
  118.   def ignore(m, params)
  119.     action = params[:action]
  120.     user = params[:option]
  121.     case action
  122.     when 'remove':
  123.       if @bot.config['markov.ignore'].include? user
  124.         s = @bot.config['markov.ignore']
  125.         s.delete user
  126.         @bot.config['ignore'] = s
  127.         m.reply "#{user} removed"
  128.       else
  129.         m.reply "not found in list"
  130.       end
  131.     when 'add':
  132.       if user
  133.         if @bot.config['markov.ignore'].include?(user)
  134.           m.reply "#{user} already in list"
  135.         else
  136.           @bot.config['markov.ignore'] = @bot.config['markov.ignore'].push user
  137.           m.reply "#{user} added to markov ignore list"
  138.         end
  139.       else
  140.         m.reply "give the name of a person or channel to ignore"
  141.       end
  142.     when 'list':
  143.       m.reply "I'm ignoring #{@bot.config['markov.ignore'].join(", ")}"
  144.     else
  145.       m.reply "have markov ignore the input from a hostmask or a channel.  usage: markov ignore add <mask or channel>; markov ignore remove <mask or channel>; markov ignore list"
  146.     end
  147.   end
  148.  
  149.   def enable(m, params)
  150.     @bot.config['markov.enabled'] = true
  151.     m.okay
  152.   end
  153.  
  154.   def probability(m, params)
  155.     if params[:probability]
  156.       @bot.config['markov.probability'] = params[:probability].to_i
  157.       m.okay
  158.     else
  159.       m.reply _("markov has a %{prob}% chance of chipping in") % { :prob => probability? }
  160.     end
  161.   end
  162.  
  163.   def disable(m, params)
  164.     @bot.config['markov.enabled'] = false
  165.     m.okay
  166.   end
  167.  
  168.   def should_talk
  169.     return false unless @bot.config['markov.enabled']
  170.     prob = probability?
  171.     return true if prob > rand(100)
  172.     return false
  173.   end
  174.  
  175.   def delay
  176.     1 + rand(5)
  177.   end
  178.  
  179.   def random_markov(m, message)
  180.     return unless should_talk
  181.  
  182.     word1, word2 = message.split(/\s+/)
  183.     return unless word1 and word2
  184.     line = generate_string(word1, word2)
  185.     return unless line
  186.     # we do nothing if the line we return is just an initial substring
  187.     # of the line we received
  188.     return if message.index(line) == 0
  189.     @bot.timer.add_once(delay) {
  190.       m.plainreply line
  191.     }
  192.   end
  193.  
  194.   def chat(m, params)
  195.     line = generate_string(params[:seed1], params[:seed2])
  196.     if line != "#{params[:seed1]} #{params[:seed2]}"
  197.       m.reply line
  198.     else
  199.       m.reply "I can't :("
  200.     end
  201.   end
  202.  
  203.   def rand_chat(m, params)
  204.     # pick a random pair from the db and go from there
  205.     word1, word2 = :nonword, :nonword
  206.     output = Array.new
  207.     50.times do
  208.       wordlist = @registry["#{word1} #{word2}"]
  209.       break if wordlist.empty?
  210.       word3 = wordlist[rand(wordlist.length)]
  211.       break if word3 == :nonword
  212.       output << word3
  213.       word1, word2 = word2, word3
  214.     end
  215.     if output.length > 1
  216.       m.reply output.join(" ")
  217.     else
  218.       m.reply "I can't :("
  219.     end
  220.   end
  221.  
  222.   def message(m)
  223.     return if ignore? m
  224.  
  225.     # in channel message, the kind we are interested in
  226.     message = clean_str m.plainmessage
  227.  
  228.     if m.action?
  229.       message = "#{m.sourcenick} #{message}"
  230.     end
  231.    
  232.     @learning_queue.push message
  233.     random_markov(m, message) unless m.replied?
  234.   end
  235.  
  236.   def learn(message)
  237.     # debug "learning #{message}"
  238.     wordlist = message.split(/\s+/)
  239.     return unless wordlist.length >= 2
  240.     word1, word2 = :nonword, :nonword
  241.     wordlist.each do |word3|
  242.       k = "#{word1} #{word2}"
  243.       @registry[k] = @registry[k].push(word3)
  244.       word1, word2 = word2, word3
  245.     end
  246.     k = "#{word1} #{word2}"
  247.     @registry[k] = @registry[k].push(:nonword)
  248.   end
  249.  
  250. def learn_url(m, params)
  251. Utils.safe_exec("w3m -cols 10000 -dump '#{params[:url]}'").split(/[\r\n]+/).each {|l| @learning_queue.push l }
  252. m.okay
  253. end
  254. end
  255.  
  256. plugin = MarkovPlugin.new
  257. plugin.map 'markov ignore :action :option', :action => "ignore"
  258. plugin.map 'markov ignore :action', :action => "ignore"
  259. plugin.map 'markov ignore', :action => "ignore"
  260. plugin.map 'markov enable', :action => "enable"
  261. plugin.map 'markov disable', :action => "disable"
  262. plugin.map 'markov status', :action => "status"
  263. plugin.map 'chat about :seed1 :seed2', :action => "chat"
  264. plugin.map 'chat', :action => "rand_chat"
  265. plugin.map 'markov probability [:probability]', :action => "probability",
  266.            :requirements => {:probability => /^\d+%?$/}
  267. plugin.map 'markov learn :url', :action => "learn_url"
Add Comment
Please, Sign In to add comment