]> git.netwichtig.de Git - user/henk/code/ruby/rbot.git/blobdiff - data/rbot/plugins/salut.rb
plugin(salut): use registry for storage see #42
[user/henk/code/ruby/rbot.git] / data / rbot / plugins / salut.rb
index b0bf67e7769fd47d0cc5ecf80d75bc93161e6383..6c6f3bc11b928676522b710b9b374421c0eb17bf 100644 (file)
-# vim: set sw=2 et:\r
-# Salutations plugin: respond to salutations\r
-# TODO allow online editing of salutations\r
-# TODO *REMEMBER* to set @changed to true after edit\r
-# TODO or changes won't be saved\r
-\r
-class SalutPlugin < Plugin\r
-  BotConfig.register BotConfigBooleanValue.new('salut.all_languages',\r
-    :default => true, \r
-    :desc => "Check for a salutation in all languages and not just in the one defined by core.language",\r
-    :on_change => Proc.new {|bot, v| bot.plugins['salut'].reload}\r
-  )\r
-  BotConfig.register BotConfigBooleanValue.new('salut.address_only',\r
-    :default => true, \r
-    :desc => "When set to true, the bot will only reply to salutations directed at him",\r
-    :on_change => Proc.new {|bot, v| bot.plugins['salut'].reload}\r
-  )\r
-\r
-\r
-  def initialize\r
-    @salutations = Hash.new\r
-    @match = Hash.new\r
-    @match_langs = Array.new\r
-    @main_lang_str = nil\r
-    @main_lang = nil\r
-    @all_langs = true\r
-    @changed = false\r
-    super\r
-    reload\r
-  end\r
-\r
-  def set_language(what)\r
-    reload\r
-  end\r
-\r
-  def create_match\r
-    @match.clear\r
-    ar_dest = Array.new\r
-    ar_in = Array.new\r
-    ar_out = Array.new\r
-    ar_both = Array.new\r
-    @salutations.each { |lang, hash|\r
-      ar_dest.clear\r
-      ar_in.clear\r
-      ar_out.clear\r
-      ar_both.clear\r
-      hash.each { |situation, array|\r
-        case situation.to_s\r
-        when /^generic-dest$/\r
-          ar_dest += array\r
-        when /in$/\r
-          ar_in += array\r
-        when /out$/\r
-          ar_out += array\r
-        else\r
-          ar_both += array\r
-        end\r
-      }\r
-      @match[lang] = Hash.new\r
-      @match[lang][:in] = Regexp.new("\\b(?:" + ar_in.uniq.map { |txt|\r
-        Regexp.escape(txt)\r
-      }.join('|') + ")\\b", Regexp::IGNORECASE) unless ar_in.empty?\r
-      @match[lang][:out] = Regexp.new("\\b(?:" + ar_out.uniq.map { |txt|\r
-        Regexp.escape(txt)\r
-      }.join('|') + ")\\b", Regexp::IGNORECASE) unless ar_out.empty?\r
-      @match[lang][:both] = Regexp.new("\\b(?:" + ar_both.uniq.map { |txt|\r
-        Regexp.escape(txt)\r
-      }.join('|') + ")\\b", Regexp::IGNORECASE) unless ar_both.empty?\r
-      @match[lang][:dest] = Regexp.new("\\b(?:" + ar_dest.uniq.map { |txt|\r
-        Regexp.escape(txt)\r
-      }.join('|') + ")\\b", Regexp::IGNORECASE) unless ar_dest.empty?\r
-    }\r
-    @punct = /\s*[.,:!;?]?\s*/ # Punctuation\r
-\r
-    # Languages to match for, in order\r
-    @match_langs.clear\r
-    @match_langs << @main_lang if @match.key?(@main_lang)\r
-    @match_langs << :english if @match.key?(:english)\r
-    @match.each_key { |key|\r
-      @match_langs << key\r
-    }\r
-    @match_langs.uniq!\r
-  end\r
-\r
-  def listen(m)\r
-    return if @match.empty?\r
-    return unless m.kind_of?(PrivMessage)\r
-    return if m.address? and m.plugin == 'config'\r
-    to_me = m.address? || m.message =~ /#{Regexp.escape(@bot.nick)}/i\r
-    if @bot.config['salut.address_only']\r
-      return unless to_me\r
-    end\r
-    salut = nil\r
-    @match_langs.each { |lang|\r
-      [:both, :in, :out].each { |k|\r
-        next unless @match[lang][k]\r
-        if m.message =~ @match[lang][k]\r
-          salut = [@match[lang][k], lang, k]\r
-          break\r
-        end\r
-      }\r
-      break if salut\r
-    }\r
-    return unless salut\r
-    # If the bot wasn't addressed, we continue only if the match was exact\r
-    # (apart from space and punctuation) or if @match[:dest] matches too\r
-    return unless to_me or m.message =~ /^#{@punct}#{salut.first}#{@punct}$/ or m.message =~ @match[salut[1]][:dest] \r
-    h = Time.new.hour\r
-    case h\r
-    when 4...12\r
-      salut_reply(m, salut, :morning)\r
-    when 12...18\r
-      salut_reply(m, salut, :afternoon)\r
-    else\r
-      salut_reply(m, salut, :evening)\r
-    end\r
-  end\r
-\r
-  def salut_reply(m, salut, time)\r
-    lang = salut[1]\r
-    k = salut[2]\r
-    debug "Replying to #{salut.first} (#{lang} #{k}) in the #{time}"\r
-    # salut_ar = @salutations[@main_lang].update @salutations[:english].update @salutations[lang]\r
-    salut_ar = @salutations[lang]\r
-    case k\r
-    when :both\r
-      sfx = ""\r
-    else\r
-      sfx = "-#{k}"\r
-    end\r
-    debug "Building array ..."\r
-    rep_ar = Array.new\r
-    rep_ar += salut_ar.fetch("#{time}#{sfx}".to_sym, [])\r
-    rep_ar += salut_ar.fetch("#{time}".to_sym, []) unless sfx.empty?\r
-    rep_ar += salut_ar.fetch("generic#{sfx}".to_sym, [])\r
-    rep_ar += salut_ar.fetch("generic".to_sym, []) unless sfx.empty?\r
-    debug "Choosing reply in #{rep_ar.inspect} ..."\r
-    if rep_ar.empty?\r
-      if m.public? # and (m.address? or m =~ /#{Regexp.escape(@bot.nick)}/)\r
-        choice = @bot.lang.get("hello_X") % m.sourcenick\r
-      else\r
-        choice = @bot.lang.get("hello") % m.sourcenick\r
-      end\r
-    else\r
-      choice = rep_ar.pick_one\r
-      if m.public? and (m.address? or m.message =~ /#{Regexp.escape(@bot.nick)}/)\r
-        choice += "#{[',',''].pick_one} #{m.sourcenick}"\r
-        choice += [" :)", " :D", "!", "", "", ""].pick_one\r
-      end\r
-    end\r
-    debug "Replying #{choice}"\r
-    m.plainreply choice\r
-  end\r
-\r
-  def reload\r
-    save\r
-    @main_lang_str = @bot.config['core.language']\r
-    @main_lang = @main_lang_str.to_sym\r
-    @all_langs = @bot.config['salut.all_languages']\r
-    if @all_langs\r
-      # Get all available languages\r
-      langs = Dir.new("#{@bot.botclass}/salut").collect {|f|\r
-        f =~ /salut-([^.]+)/ ? $1 : nil\r
-      }.compact\r
-      langs.each { |lang|\r
-        @salutations[lang.to_sym] = load_lang(lang)\r
-      }\r
-    else\r
-      @salutations.clear\r
-      @salutations[@main_lang] = load_lang(@main_lang_str)\r
-    end\r
-    create_match\r
-    @changed = false\r
-  end\r
-\r
-  def load_lang(lang)\r
-    dir = "#{@bot.botclass}/salut"\r
-    if not File.exist?(dir)\r
-      Dir.mkdir(dir)\r
-    end\r
-    file = "#{@bot.botclass}/salut/salut-#{lang}"\r
-    if File.exist?(file)\r
-      begin\r
-        salutations = Hash.new\r
-        content = YAML::load_file(file)\r
-        content.each { |key, val|\r
-          salutations[key.to_sym] = val\r
-        }\r
-        return salutations\r
-      rescue\r
-        error "failed to read salutations in #{lang}: #{$!}"\r
-      end\r
-    end\r
-    return nil\r
-  end\r
-\r
-  def save\r
-    return if @salutations.empty?\r
-    return unless @changed\r
-    @salutations.each { |lang, val|\r
-      l = lang.to_s\r
-      save_lang(lang, val)\r
-    }\r
-    @changed = false\r
-  end\r
-\r
-  def save_lang(lang, val)\r
-    fn = "#{@bot.botclass}/salut/salut-#{lang}"\r
-    Utils.safe_save(fn) { |file|\r
-      file.puts val.to_yaml\r
-    }\r
-  end\r
-\r
-end\r
-\r
-plugin = SalutPlugin.new\r
-\r
+#-- vim:sw=2:et
+#++
+#
+# :title: Salutations plugin for rbot
+#
+# Author:: Giuseppe "Oblomov" Bilotta <giuseppe.bilotta@gmail.com>
+# Copyright:: (C) 2006-2007 Giuseppe Bilotta
+# License:: GPL v2
+#
+# Salutations plugin: respond to salutations
+#
+# TODO:: allow online editing of salutations
+
+class SalutPlugin < Plugin
+  Config.register Config::BooleanValue.new('salut.all_languages',
+    :default => true,
+    :desc => "Check for a salutation in all languages and not just in the one defined by core.language",
+    :on_change => Proc.new {|bot, v| bot.plugins['salut'].reload})
+
+  Config.register Config::BooleanValue.new('salut.address_only',
+    :default => true,
+    :desc => "When set to true, the bot will only reply to salutations directed at him",
+    :on_change => Proc.new {|bot, v| bot.plugins['salut'].reload})
+
+  def initialize
+    super
+
+    @match = Hash.new
+    @match_langs = Array.new
+
+    reload
+  end
+
+  def set_language(language)
+    @language = language
+  end
+
+  def load_static_files(path)
+    debug "loading salutation rules from #{path}"
+    Dir.glob("#{path}/*").map { |filename|
+      language = filename[filename.rindex('-')+1..-1]
+      begin
+        salutations = {}
+        content = YAML::load_file(filename)
+        content.each { |key, val|
+          salutations[key.to_sym] = val
+        }
+      rescue
+        error "failed to read salutations in #{filename}: #{$!}"
+      end
+      [language, salutations]
+    }.to_h
+  end
+
+  def reload
+    @salutations = @registry[:salutations]
+
+    # migrate existing data files
+    if not @salutations and Dir.exists? datafile
+      log "migrate existing salutations from #{datafile}"
+
+      @salutations = load_static_files(datafile)
+    end
+
+    # load initial salutations from plugin directory
+    unless @salutations
+      log "load initial salutations from #{plugin_path}"
+
+      initial_path = File.join(plugin_path, 'salut')
+      @salutations = load_static_files(initial_path)
+    end
+
+    debug @salutations.inspect
+
+    create_match
+  end
+
+  def save
+    return unless @salutations
+
+    @registry[:salutations] = @salutations
+
+    @registry.flush
+  end
+
+  def create_match
+    use_all_languages = @bot.config['salut.all_languages']
+
+    @match.clear
+    ar_dest = Array.new
+    ar_in = Array.new
+    ar_out = Array.new
+    ar_both = Array.new
+    @salutations.each { |lang, hash|
+      next if lang != @language and not use_all_languages
+      ar_dest.clear
+      ar_in.clear
+      ar_out.clear
+      ar_both.clear
+      hash.each { |situation, array|
+        case situation.to_s
+        when /^generic-dest$/
+          ar_dest += array
+        when /in$/
+          ar_in += array
+        when /out$/
+          ar_out += array
+        else
+          ar_both += array
+        end
+      }
+      @match[lang] = Hash.new
+      @match[lang][:in] = Regexp.new("\\b(?:" + ar_in.uniq.map { |txt|
+        Regexp.escape(txt)
+      }.join('|') + ")\\b", Regexp::IGNORECASE) unless ar_in.empty?
+      @match[lang][:out] = Regexp.new("\\b(?:" + ar_out.uniq.map { |txt|
+        Regexp.escape(txt)
+      }.join('|') + ")\\b", Regexp::IGNORECASE) unless ar_out.empty?
+      @match[lang][:both] = Regexp.new("\\b(?:" + ar_both.uniq.map { |txt|
+        Regexp.escape(txt)
+      }.join('|') + ")\\b", Regexp::IGNORECASE) unless ar_both.empty?
+      @match[lang][:dest] = Regexp.new("\\b(?:" + ar_dest.uniq.map { |txt|
+        Regexp.escape(txt)
+      }.join('|') + ")\\b", Regexp::IGNORECASE) unless ar_dest.empty?
+    }
+    @punct = /\s*[.,:!;?]?\s*/ # Punctuation
+
+    # Languages to match for, in order
+    @match_langs.clear
+    @match_langs << @language if @match.key?(@language)
+    @match_langs << 'english' if @match.key?('english')
+    @match.each_key { |key|
+      @match_langs << key
+    }
+    @match_langs.uniq!
+  end
+
+  def unreplied(m)
+    return if @match.empty?
+    return unless m.kind_of?(PrivMessage)
+    return if m.address? and m.plugin == 'config'
+    to_me = m.address? || m.message =~ /#{Regexp.escape(@bot.nick)}/i
+    if @bot.config['salut.address_only']
+      return unless to_me
+    end
+    salut = nil
+    @match_langs.each { |lang|
+      [:both, :in, :out].each { |k|
+        next unless @match[lang][k]
+        if m.message =~ @match[lang][k]
+          salut = [@match[lang][k], lang, k]
+          break
+        end
+      }
+      break if salut
+    }
+    return unless salut
+    # If the bot wasn't addressed, we continue only if the match was exact
+    # (apart from space and punctuation) or if @match[:dest] matches too
+    return unless to_me or m.message =~ /^#{@punct}#{salut.first}#{@punct}$/ or m.message =~ @match[salut[1]][:dest]
+    h = Time.new.hour
+    case h
+    when 4...12
+      salut_reply(m, salut, :morning)
+    when 12...18
+      salut_reply(m, salut, :afternoon)
+    else
+      salut_reply(m, salut, :evening)
+    end
+  end
+
+  def salut_reply(m, salut, time)
+    lang = salut[1]
+    k = salut[2]
+    debug "Replying to #{salut.first} (#{lang} #{k}) in the #{time}"
+    salut_ar = @salutations[lang]
+    case k
+    when :both
+      sfx = ""
+    else
+      sfx = "-#{k}"
+    end
+    debug "Building array ..."
+    rep_ar = Array.new
+    rep_ar += salut_ar.fetch("#{time}#{sfx}".to_sym, [])
+    rep_ar += salut_ar.fetch("#{time}".to_sym, []) unless sfx.empty?
+    rep_ar += salut_ar.fetch("generic#{sfx}".to_sym, [])
+    rep_ar += salut_ar.fetch("generic".to_sym, []) unless sfx.empty?
+    debug "Choosing reply in #{rep_ar.inspect} ..."
+    if rep_ar.empty?
+      if m.public? # and (m.address? or m =~ /#{Regexp.escape(@bot.nick)}/)
+        choice = @bot.lang.get("hello_X") % m.sourcenick
+      else
+        choice = @bot.lang.get("hello") % m.sourcenick
+      end
+    else
+      choice = rep_ar.pick_one
+      if m.public? and (m.address? or m.message =~ /#{Regexp.escape(@bot.nick)}/)
+        choice += "#{[',',''].pick_one} #{m.sourcenick}"
+        choice += [" :)", " :D", "!", "", "", ""].pick_one
+      end
+    end
+    debug "Replying #{choice}"
+    m.reply choice, :nick => false, :to => :public
+  end
+end
+
+SalutPlugin.new