]> git.netwichtig.de Git - user/henk/code/ruby/rbot.git/blobdiff - lib/rbot/core/config.rb
introduce reload alias for rescan
[user/henk/code/ruby/rbot.git] / lib / rbot / core / config.rb
index 7ff7a88c96d02fc2e38dcc063e739f08339960f1..43d08829884a5606c64206129f5b36494f7c6061 100644 (file)
-#-- vim:sw=2:et\r
-#++\r
-#\r
-# :title: rbot config management from IRC\r
-#\r
-# Author:: Giuseppe "Oblomov" Bilotta <giuseppe.bilotta@gmail.com>\r
-# Copyright:: (C) 2006,2007 Giuseppe Bilotta\r
-# License:: GPL v2\r
-\r
-class ConfigModule < CoreBotModule\r
-\r
-  def version_string\r
-    _("I'm a v. %{version} rubybot%{copyright}%{url}") % {\r
-      :version => $version,\r
-      :copyright => ", #{Irc::Bot::COPYRIGHT_NOTICE}",\r
-      :url => " - #{Irc::Bot::SOURCE_URL}"\r
-    }\r
-  end\r
-\r
-  def save\r
-    @bot.config.save\r
-  end\r
-\r
-  def handle_list(m, params)\r
-    modules = []\r
-    if params[:module]\r
-      @bot.config.items.each_key do |key|\r
-        mod, name = key.to_s.split('.')\r
-        next unless mod == params[:module]\r
-        modules.push key unless modules.include?(name)\r
-      end\r
-      if modules.empty?\r
-        m.reply _("no such module %{module}") % {:module => params[:module]}\r
-      else\r
-        m.reply modules.join(", ")\r
-      end\r
-    else\r
-      @bot.config.items.each_key do |key|\r
-        name = key.to_s.split('.').first\r
-        modules.push name unless modules.include?(name)\r
-      end\r
-      m.reply "modules: " + modules.join(", ")\r
-    end\r
-  end\r
-\r
-  def handle_get(m, params)\r
-    key = params[:key].to_s.intern\r
-    unless @bot.config.items.has_key?(key)\r
-      m.reply _("no such config key %{key}") % {:key => key}\r
-      return\r
-    end\r
-    return if !@bot.auth.allow?(@bot.config.items[key].auth_path, m.source, m.replyto)\r
-    value = @bot.config.items[key].to_s\r
-    m.reply "#{key}: #{value}"\r
-  end\r
-\r
-  def handle_desc(m, params)\r
-    key = params[:key].to_s.intern\r
-    unless @bot.config.items.has_key?(key)\r
-      m.reply _("no such config key %{key}") % {:key => key}\r
-    end\r
-    m.reply "#{key}: #{@bot.config.items[key].desc}"\r
-  end\r
-\r
-  def handle_unset(m, params)\r
-    key = params[:key].to_s.intern\r
-    unless @bot.config.items.has_key?(key)\r
-      m.reply _("no such config key %{key}") % {:key => key}\r
-    end\r
-    return if !@bot.auth.allow?(@bot.config.items[key].auth_path, m.source, m.replyto)\r
-    @bot.config.items[key].unset\r
-    handle_get(m, params)\r
-    m.reply _("this config change will take effect on the next restart") if @bot.config.items[key].requires_restart\r
-    m.reply _("this config change will take effect on the next rescan") if @bot.config.items[key].requires_rescan\r
-  end\r
-\r
-  def handle_set(m, params)\r
-    key = params[:key].to_s.intern\r
-    value = params[:value].join(" ")\r
-    unless @bot.config.items.has_key?(key)\r
-      m.reply _("no such config key %{key}") % {:key => key} unless params[:silent]\r
-      return false\r
-    end\r
-    return false if !@bot.auth.allow?(@bot.config.items[key].auth_path, m.source, m.replyto)\r
-    begin\r
-      @bot.config.items[key].set_string(value)\r
-    rescue ArgumentError => e\r
-      m.reply _("failed to set %{key}: %{error}") % {:key => key, :error => e.message} unless params[:silent]\r
-      return false\r
-    end\r
-    if @bot.config.items[key].requires_restart\r
-      m.reply _("this config change will take effect on the next restart") unless params[:silent]\r
-      return :restart\r
-    elsif @bot.config.items[key].requires_rescan\r
-      m.reply _("this config change will take effect on the next rescan") unless params[:silent]\r
-      return :rescan\r
-    else\r
-      m.okay unless params[:silent]\r
-      return true\r
-    end\r
-  end\r
-\r
-  def handle_add(m, params)\r
-    key = params[:key].to_s.intern\r
-    value = params[:value]\r
-    unless @bot.config.items.has_key?(key)\r
-      m.reply _("no such config key %{key}") % {:key => key}\r
-      return\r
-    end\r
-    unless @bot.config.items[key].kind_of?(BotConfigArrayValue)\r
-      m.reply _("config key %{key} is not an array") % {:key => key}\r
-      return\r
-    end\r
-    return if !@bot.auth.allow?(@bot.config.items[key].auth_path, m.source, m.replyto)\r
-    begin\r
-      @bot.config.items[key].add(value)\r
-    rescue ArgumentError => e\r
-      m.reply _("failed to add %{value} to %{key}: %{error}") % {:value => value, :key => key, :error => e.message}\r
-      return\r
-    end\r
-    handle_get(m,{:key => key})\r
-    m.reply _("this config change will take effect on the next restart") if @bot.config.items[key].requires_restart\r
-    m.reply _("this config change will take effect on the next rescan") if @bot.config.items[key].requires_rescan\r
-  end\r
-\r
-  def handle_rm(m, params)\r
-    key = params[:key].to_s.intern\r
-    value = params[:value]\r
-    unless @bot.config.items.has_key?(key)\r
-      m.reply _("no such config key %{key}") % {:key => key}\r
-      return\r
-    end\r
-    unless @bot.config.items[key].kind_of?(BotConfigArrayValue)\r
-      m.reply _("config key %{key} is not an array") % {:key => key}\r
-      return\r
-    end\r
-    return if !@bot.auth.allow?(@bot.config.items[key].auth_path, m.source, m.replyto)\r
-    begin\r
-      @bot.config.items[key].rm(value)\r
-    rescue ArgumentError => e\r
-      m.reply _("failed to remove %{value} from %{key}: %{error}") % {:value => value, :key => key, :error => e.message}\r
-      return\r
-    end\r
-    handle_get(m,{:key => key})\r
-    m.reply _("this config change will take effect on the next restart") if @bot.config.items[key].requires_restart\r
-    m.reply _("this config change will take effect on the next rescan") if @bot.config.items[key].requires_rescan\r
-  end\r
-\r
-  def bot_save(m, param)\r
-    @bot.save\r
-    m.okay\r
-  end\r
-\r
-  def bot_rescan(m, param)\r
-    m.reply _("saving ...")\r
-    @bot.save\r
-    m.reply _("rescanning ...")\r
-    @bot.rescan\r
-    m.reply _("done. %{plugin_status}") % {:plugin_status => @bot.plugins.status(true)}\r
-  end\r
-\r
-  def bot_nick(m, param)\r
-    @bot.nickchg(param[:nick])\r
-  end\r
-\r
-  def bot_status(m, param)\r
-    m.reply @bot.status\r
-  end\r
-\r
-  # TODO is this one of the methods that disappeared when the bot was moved\r
-  # from the single-file to the multi-file registry?\r
-  #\r
-  #  def bot_reg_stat(m, param)\r
-  #    m.reply @registry.stat.inspect\r
-  #  end\r
-\r
-  def bot_version(m, param)\r
-    m.reply version_string\r
-  end\r
-\r
-  def handle_help(m, params)\r
-    m.reply help(params[:topic])\r
-  end\r
-\r
-  def help(plugin, topic="")\r
-    case plugin\r
-    when "config"\r
-      case topic\r
-      when ""\r
-      _("config-related tasks: config topics, save, rescan")\r
-      when "list"\r
-      _("config list => list configuration modules, config list <module> => list configuration keys for module <module>")\r
-      when "get"\r
-      _("config get <key> => get configuration value for key <key>")\r
-      when "unset"\r
-      _("reset key <key> to the default")\r
-      when "set"\r
-      _("config set <key> <value> => set configuration value for key <key> to <value>")\r
-      when "desc"\r
-      _("config desc <key> => describe what key <key> configures")\r
-      when "add"\r
-      _("config add <value> to <key> => add value <value> to key <key> if <key> is an array")\r
-      when "rm"\r
-      _("config rm <value> from <key> => remove value <value> from key <key> if <key> is an array")\r
-      else\r
-      _("config module - bot configuration. usage: list, desc, get, set, unset, add, rm")\r
-      # else\r
-      #   "no help for config #{topic}"\r
-      end\r
-    when "save"\r
-      _("save => save current dynamic data and configuration")\r
-    when "rescan"\r
-      _("rescan => reload modules and static facts")\r
-    when "version"\r
-      _("version => describes software version")\r
-    else\r
-      _("config-related tasks: config, save, rescan, version")\r
-    end\r
-  end\r
-\r
-end\r
-\r
-conf = ConfigModule.new\r
-\r
-conf.map 'config list :module',\r
-  :action => 'handle_list',\r
-  :defaults => {:module => false},\r
-  :auth_path => 'show'\r
-# TODO this one is presently a security risk, since the bot\r
-# stores the master password in the config. Do we need auth levels\r
-# on the BotConfig keys too?\r
-conf.map 'config get :key',\r
-  :action => 'handle_get',\r
-  :auth_path => 'show'\r
-conf.map 'config desc :key',\r
-  :action => 'handle_desc',\r
-  :auth_path => 'show'\r
-conf.map 'config describe :key',\r
-  :action => 'handle_desc',\r
-  :auth_path => 'show'\r
-\r
-conf.map "save",\r
-  :action => 'bot_save'\r
-conf.map "rescan",\r
-  :action => 'bot_rescan'\r
-conf.map "nick :nick",\r
-  :action => 'bot_nick'\r
-conf.map "status",\r
-  :action => 'bot_status',\r
-  :auth_path => 'show::status'\r
-# TODO see above\r
-#\r
-# conf.map "registry stats",\r
-#   :action => 'bot_reg_stat',\r
-#   :auth_path => '!config::status'\r
-conf.map "version",\r
-  :action => 'bot_version',\r
-  :auth_path => 'show::status'\r
-\r
-conf.map 'config set :key *value',\r
-  :action => 'handle_set',\r
-  :auth_path => 'edit'\r
-conf.map 'config add :value to :key',\r
-  :action => 'handle_add',\r
-  :auth_path => 'edit'\r
-conf.map 'config rm :value from :key',\r
-  :action => 'handle_rm',\r
-  :auth_path => 'edit'\r
-conf.map 'config del :value from :key',\r
-  :action => 'handle_rm',\r
-  :auth_path => 'edit'\r
-conf.map 'config delete :value from :key',\r
-  :action => 'handle_rm',\r
-  :auth_path => 'edit'\r
-conf.map 'config unset :key',\r
-  :action => 'handle_unset',\r
-  :auth_path => 'edit'\r
-conf.map 'config reset :key',\r
-  :action => 'handle_unset',\r
-  :auth_path => 'edit'\r
-\r
-conf.map 'config help :topic',\r
-  :action => 'handle_help',\r
-  :defaults => {:topic => false},\r
-  :auth_path => '!help!'\r
-\r
-conf.default_auth('*', false)\r
-conf.default_auth('show::status', true)\r
-# TODO these shouldn't be set here, we need a way to let the default\r
-# permission be specified together with the BotConfigValue\r
-conf.default_auth('key', true)\r
-conf.default_auth('key::auth::password', false)\r
-\r
+#-- vim:sw=2:et
+#++
+#
+# :title: rbot config management from IRC
+#
+# Author:: Giuseppe "Oblomov" Bilotta <giuseppe.bilotta@gmail.com>
+
+class ConfigModule < CoreBotModule
+
+  def version_string
+    if $version_timestamp.to_i > 0
+      ago = _(" [%{secs} ago]") % {
+        :secs => Utils.secs_to_string(Time.now.to_i - $version_timestamp.to_i)
+      }
+    else
+      ago = ''
+    end
+    _("I'm a v. %{version}%{ago} rubybot%{copyright}%{url}") % {
+      :version => $version,
+      :ago => ago,
+      :copyright => ", #{Irc::Bot::COPYRIGHT_NOTICE}",
+      :url => " - #{Irc::Bot::SOURCE_URL}"
+    }
+  end
+
+  def save
+    @bot.config.save
+  end
+
+  def handle_list(m, params)
+    modules = []
+    if params[:module]
+      @bot.config.items.each_key do |key|
+        mod, name = key.to_s.split('.')
+        next unless mod == params[:module]
+        modules.push key unless modules.include?(name)
+      end
+      if modules.empty?
+        m.reply _("no such module %{module}") % {:module => params[:module]}
+      else
+        m.reply modules.join(", ")
+      end
+    else
+      @bot.config.items.each_key do |key|
+        name = key.to_s.split('.').first
+        modules.push name unless modules.include?(name)
+      end
+      m.reply "modules: " + modules.join(", ")
+    end
+  end
+
+  def handle_get(m, params)
+    key = params[:key].to_s.intern
+    unless @bot.config.items.has_key?(key)
+      m.reply _("no such config key %{key}") % {:key => key}
+      return
+    end
+    return if !@bot.auth.allow?(@bot.config.items[key].auth_path, m.source, m.replyto)
+    value = @bot.config.items[key].to_s
+    m.reply "#{key}: #{value}"
+  end
+
+  def handle_desc(m, params)
+    key = params[:key].to_s.intern
+    unless @bot.config.items.has_key?(key)
+      m.reply _("no such config key %{key}") % {:key => key}
+    end
+    m.reply "#{key}: #{@bot.config.items[key].desc}"
+  end
+
+  def handle_search(m, params)
+    rx = Regexp.new(params[:rx].to_s, true)
+    cfs = []
+    @bot.config.items.each do |k, v|
+      cfs << [Bold + k.to_s + Bold, v.desc] if k.to_s.match(rx) or (v.desc.match(rx) rescue false)
+    end
+    if cfs.empty?
+      m.reply _("no config key found matching %{r}") % { :r => params[:rx].to_s}
+    else
+      m.reply _("possible keys: %{kl}") % { :kl => cfs.map { |c| c.first}.sort.join(', ') } if cfs.length > 1
+      m.reply cfs.map { |c| c.join(': ') }.join("\n")
+    end
+  end
+
+  def handle_unset(m, params)
+    key = params[:key].to_s.intern
+    unless @bot.config.items.has_key?(key)
+      m.reply _("no such config key %{key}") % {:key => key}
+    end
+    return if !@bot.auth.allow?(@bot.config.items[key].auth_path, m.source, m.replyto)
+    @bot.config.items[key].unset
+    handle_get(m, params)
+    m.reply _("this config change will take effect on the next restart") if @bot.config.items[key].requires_restart
+    m.reply _("this config change will take effect on the next rescan") if @bot.config.items[key].requires_rescan
+  end
+
+  def handle_set(m, params)
+    key = params[:key].to_s.intern
+    value = params[:value].join(" ")
+    unless @bot.config.items.has_key?(key)
+      m.reply _("no such config key %{key}") % {:key => key} unless params[:silent]
+      return false
+    end
+    return false if !@bot.auth.allow?(@bot.config.items[key].auth_path, m.source, m.replyto)
+    begin
+      @bot.config.items[key].set_string(value)
+    rescue ArgumentError => e
+      m.reply _("failed to set %{key}: %{error}") % {:key => key, :error => e.message} unless params[:silent]
+      return false
+    end
+    if @bot.config.items[key].requires_restart
+      m.reply _("this config change will take effect on the next restart") unless params[:silent]
+      return :restart
+    elsif @bot.config.items[key].requires_rescan
+      m.reply _("this config change will take effect on the next rescan") unless params[:silent]
+      return :rescan
+    else
+      m.okay unless params[:silent]
+      return true
+    end
+  end
+
+  def handle_add(m, params)
+    key = params[:key].to_s.intern
+    values = params[:value].to_s.split(/,\s+/)
+    unless @bot.config.items.has_key?(key)
+      m.reply _("no such config key %{key}") % {:key => key}
+      return
+    end
+    unless @bot.config.items[key].kind_of?(Config::ArrayValue)
+      m.reply _("config key %{key} is not an array") % {:key => key}
+      return
+    end
+    return if !@bot.auth.allow?(@bot.config.items[key].auth_path, m.source, m.replyto)
+    values.each do |value|
+      begin
+        @bot.config.items[key].add(value)
+      rescue ArgumentError => e
+        m.reply _("failed to add %{value} to %{key}: %{error}") % {:value => value, :key => key, :error => e.message}
+        next
+      end
+    end
+    handle_get(m,{:key => key})
+    m.reply _("this config change will take effect on the next restart") if @bot.config.items[key].requires_restart
+    m.reply _("this config change will take effect on the next rescan") if @bot.config.items[key].requires_rescan
+  end
+
+  def handle_rm(m, params)
+    key = params[:key].to_s.intern
+    values = params[:value].to_s.split(/,\s+/)
+    unless @bot.config.items.has_key?(key)
+      m.reply _("no such config key %{key}") % {:key => key}
+      return
+    end
+    unless @bot.config.items[key].kind_of?(Config::ArrayValue)
+      m.reply _("config key %{key} is not an array") % {:key => key}
+      return
+    end
+    return if !@bot.auth.allow?(@bot.config.items[key].auth_path, m.source, m.replyto)
+    values.each do |value|
+      begin
+        @bot.config.items[key].rm(value)
+      rescue ArgumentError => e
+        m.reply _("failed to remove %{value} from %{key}: %{error}") % {:value => value, :key => key, :error => e.message}
+        next
+      end
+    end
+    handle_get(m,{:key => key})
+    m.reply _("this config change will take effect on the next restart") if @bot.config.items[key].requires_restart
+    m.reply _("this config change will take effect on the next rescan") if @bot.config.items[key].requires_rescan
+  end
+
+  def bot_save(m, param)
+    @bot.save
+    m.okay
+  end
+
+  def bot_rescan(m, param)
+    if param[:botmodule]
+      name = param[:botmodule]
+      if not @bot.plugins.has_key? name
+        m.reply _("botmodule not found")
+        return # error
+      else
+        botmodule = @bot.plugins[name]
+        m.reply _("botmodule %s... saving... rescanning...") % [name]
+      end
+    else
+      m.reply _("saving... rescanning...")
+    end
+
+    @bot.rescan(botmodule)
+    m.reply _("done. %{plugin_status}") % {
+      :plugin_status => @bot.plugins.status(true)}
+    failure = @bot.plugins.botmodule_failure(name) if botmodule
+    if failure
+      m.reply _("plugin failed to load, %{failure}") % {
+        :failure => failure}
+    end
+  end
+
+  def bot_nick(m, param)
+    @bot.nickchg(param[:nick])
+    @bot.wanted_nick = param[:nick]
+  end
+
+  def bot_status(m, param)
+    m.reply @bot.status
+  end
+
+  # TODO is this one of the methods that disappeared when the bot was moved
+  # from the single-file to the multi-file registry?
+  #
+  #  def bot_reg_stat(m, param)
+  #    m.reply @registry.stat.inspect
+  #  end
+
+  def bot_version(m, param)
+    m.reply version_string
+  end
+
+  def ctcp_listen(m)
+    who = m.private? ? "me" : m.target
+    case m.ctcp.intern
+    when :VERSION
+      m.ctcp_reply version_string
+    when :SOURCE
+      m.ctcp_reply Irc::Bot::SOURCE_URL
+    end
+  end
+
+  def handle_help(m, params)
+    m.reply help(params[:topic])
+  end
+
+  def help(plugin, topic="")
+    case plugin
+    when "config"
+      case topic
+      when "list"
+      _("config list => list configuration modules, config list <module> => list configuration keys for module <module>")
+      when "get"
+      _("config get <key> => get configuration value for key <key>")
+      when "unset"
+      _("reset key <key> to the default")
+      when "set"
+      _("config set <key> <value> => set configuration value for key <key> to <value>")
+      when "desc"
+      _("config desc <key> => describe what key <key> configures")
+      when "add"
+      _("config add <values> to <key> => add values <values> to key <key> if <key> is an array")
+      when "rm"
+      _("config rm <value> from <key> => remove value <value> from key <key> if <key> is an array")
+      else
+      _("config module - bot configuration. usage: list, desc, get, set, unset, add, rm")
+      # else
+      #   "no help for config #{topic}"
+      end
+    when "nick"
+      _("nick <newnick> => change the bot nick to <newnick>, if possible")
+    when "status"
+      _("status => display some information on the bot's status")
+    when "save"
+      _("save => save current dynamic data and configuration")
+    when "rescan"
+      _("rescan [<botmodule>] => reload specified or all botmodules and static facts")
+    when "reload"
+      _("reload [<botmodule>] => reload specified or all botmodules and static facts")
+    when "version"
+      _("version => describes software version")
+    else
+      _("config-related tasks: config, save, rescan(/reload), version, nick, status")
+    end
+  end
+
+end
+
+conf = ConfigModule.new
+
+conf.map 'config list :module',
+  :action => 'handle_list',
+  :defaults => {:module => false},
+  :auth_path => 'show'
+# TODO this one is presently a security risk, since the bot
+# stores the master password in the config. Do we need auth levels
+# on the Bot::Config keys too?
+conf.map 'config get :key',
+  :action => 'handle_get',
+  :auth_path => 'show'
+conf.map 'config desc :key',
+  :action => 'handle_desc',
+  :auth_path => 'show'
+conf.map 'config describe :key',
+  :action => 'handle_desc',
+  :auth_path => 'show::desc!'
+conf.map 'config search *rx',
+  :action => 'handle_search',
+  :auth_path => 'show'
+
+conf.map "save",
+  :action => 'bot_save'
+conf.map "rescan [:botmodule]",
+  :action => 'bot_rescan'
+conf.map "reload [:botmodule]",
+  :action => 'bot_rescan'
+conf.map "nick :nick",
+  :action => 'bot_nick'
+conf.map "status",
+  :action => 'bot_status',
+  :auth_path => 'show::status'
+# TODO see above
+#
+# conf.map "registry stats",
+#   :action => 'bot_reg_stat',
+#   :auth_path => '!config::status'
+conf.map "version",
+  :action => 'bot_version',
+  :auth_path => 'show::status'
+
+conf.map 'config set :key *value',
+  :action => 'handle_set',
+  :auth_path => 'edit'
+conf.map 'config add *value to :key',
+  :action => 'handle_add',
+  :auth_path => 'edit'
+conf.map 'config rm *value from :key',
+  :action => 'handle_rm',
+  :auth_path => 'edit'
+conf.map 'config remove *value from :key',
+  :action => 'handle_rm',
+  :auth_path => 'edit'
+conf.map 'config del *value from :key',
+  :action => 'handle_rm',
+  :auth_path => 'edit'
+conf.map 'config delete *value from :key',
+  :action => 'handle_rm',
+  :auth_path => 'edit'
+conf.map 'config unset :key',
+  :action => 'handle_unset',
+  :auth_path => 'edit'
+conf.map 'config reset :key',
+  :action => 'handle_unset',
+  :auth_path => 'edit'
+
+conf.map 'config help :topic',
+  :action => 'handle_help',
+  :defaults => {:topic => false},
+  :auth_path => '!help!'
+
+conf.default_auth('*', false)
+conf.default_auth('show', true)
+conf.default_auth('show::get', false)
+# TODO these shouldn't be set here, we need a way to let the default
+# permission be specified together with the ConfigValue
+conf.default_auth('key', true)
+conf.default_auth('key::auth::password', false)
+