4 # :title: rbot config management from IRC
6 # Author:: Giuseppe "Oblomov" Bilotta <giuseppe.bilotta@gmail.com>
7 # Copyright:: (C) 2006,2007 Giuseppe Bilotta
10 class ConfigModule < CoreBotModule
13 if $version_timestamp.to_i > 0
15 ago << Utils.secs_to_string(Time.now.to_i - $version_timestamp.to_i)
20 _("I'm a v. %{version}%{ago} rubybot%{copyright}%{url}") % {
23 :copyright => ", #{Irc::Bot::COPYRIGHT_NOTICE}",
24 :url => " - #{Irc::Bot::SOURCE_URL}"
32 def handle_list(m, params)
35 @bot.config.items.each_key do |key|
36 mod, name = key.to_s.split('.')
37 next unless mod == params[:module]
38 modules.push key unless modules.include?(name)
41 m.reply _("no such module %{module}") % {:module => params[:module]}
43 m.reply modules.join(", ")
46 @bot.config.items.each_key do |key|
47 name = key.to_s.split('.').first
48 modules.push name unless modules.include?(name)
50 m.reply "modules: " + modules.join(", ")
54 def handle_get(m, params)
55 key = params[:key].to_s.intern
56 unless @bot.config.items.has_key?(key)
57 m.reply _("no such config key %{key}") % {:key => key}
60 return if !@bot.auth.allow?(@bot.config.items[key].auth_path, m.source, m.replyto)
61 value = @bot.config.items[key].to_s
62 m.reply "#{key}: #{value}"
65 def handle_desc(m, params)
66 key = params[:key].to_s.intern
67 unless @bot.config.items.has_key?(key)
68 m.reply _("no such config key %{key}") % {:key => key}
70 m.reply "#{key}: #{@bot.config.items[key].desc}"
73 def handle_search(m, params)
74 rx = Regexp.new(params[:rx].to_s, true)
76 @bot.config.items.each do |k, v|
77 cfs << v if k.to_s.match(rx) or (v.desc.match(rx) rescue false)
80 m.reply _("no config key found matching %{r}") % { :r => params[:rx].to_s}
82 m.reply _("possible keys: %{kl}") % { :kl => cfs.map { |c| c.key}.join(', ') }
83 m.reply cfs.map { |c| [c.key, c.desc].join(': ') }.join("\n")
87 def handle_unset(m, params)
88 key = params[:key].to_s.intern
89 unless @bot.config.items.has_key?(key)
90 m.reply _("no such config key %{key}") % {:key => key}
92 return if !@bot.auth.allow?(@bot.config.items[key].auth_path, m.source, m.replyto)
93 @bot.config.items[key].unset
95 m.reply _("this config change will take effect on the next restart") if @bot.config.items[key].requires_restart
96 m.reply _("this config change will take effect on the next rescan") if @bot.config.items[key].requires_rescan
99 def handle_set(m, params)
100 key = params[:key].to_s.intern
101 value = params[:value].join(" ")
102 unless @bot.config.items.has_key?(key)
103 m.reply _("no such config key %{key}") % {:key => key} unless params[:silent]
106 return false if !@bot.auth.allow?(@bot.config.items[key].auth_path, m.source, m.replyto)
108 @bot.config.items[key].set_string(value)
109 rescue ArgumentError => e
110 m.reply _("failed to set %{key}: %{error}") % {:key => key, :error => e.message} unless params[:silent]
113 if @bot.config.items[key].requires_restart
114 m.reply _("this config change will take effect on the next restart") unless params[:silent]
116 elsif @bot.config.items[key].requires_rescan
117 m.reply _("this config change will take effect on the next rescan") unless params[:silent]
120 m.okay unless params[:silent]
125 def handle_add(m, params)
126 key = params[:key].to_s.intern
127 value = params[:value]
128 unless @bot.config.items.has_key?(key)
129 m.reply _("no such config key %{key}") % {:key => key}
132 unless @bot.config.items[key].kind_of?(Config::ArrayValue)
133 m.reply _("config key %{key} is not an array") % {:key => key}
136 return if !@bot.auth.allow?(@bot.config.items[key].auth_path, m.source, m.replyto)
138 @bot.config.items[key].add(value)
139 rescue ArgumentError => e
140 m.reply _("failed to add %{value} to %{key}: %{error}") % {:value => value, :key => key, :error => e.message}
143 handle_get(m,{:key => key})
144 m.reply _("this config change will take effect on the next restart") if @bot.config.items[key].requires_restart
145 m.reply _("this config change will take effect on the next rescan") if @bot.config.items[key].requires_rescan
148 def handle_rm(m, params)
149 key = params[:key].to_s.intern
150 value = params[:value]
151 unless @bot.config.items.has_key?(key)
152 m.reply _("no such config key %{key}") % {:key => key}
155 unless @bot.config.items[key].kind_of?(Config::ArrayValue)
156 m.reply _("config key %{key} is not an array") % {:key => key}
159 return if !@bot.auth.allow?(@bot.config.items[key].auth_path, m.source, m.replyto)
161 @bot.config.items[key].rm(value)
162 rescue ArgumentError => e
163 m.reply _("failed to remove %{value} from %{key}: %{error}") % {:value => value, :key => key, :error => e.message}
166 handle_get(m,{:key => key})
167 m.reply _("this config change will take effect on the next restart") if @bot.config.items[key].requires_restart
168 m.reply _("this config change will take effect on the next rescan") if @bot.config.items[key].requires_rescan
171 def bot_save(m, param)
176 def bot_rescan(m, param)
177 m.reply _("saving ...")
179 m.reply _("rescanning ...")
181 m.reply _("done. %{plugin_status}") % {:plugin_status => @bot.plugins.status(true)}
184 def bot_nick(m, param)
185 @bot.nickchg(param[:nick])
188 def bot_status(m, param)
192 # TODO is this one of the methods that disappeared when the bot was moved
193 # from the single-file to the multi-file registry?
195 # def bot_reg_stat(m, param)
196 # m.reply @registry.stat.inspect
199 def bot_version(m, param)
200 m.reply version_string
204 who = m.private? ? "me" : m.target
207 m.ctcp_reply version_string
208 # @bot.irclog "@ #{m.source} asked #{who} about version info"
210 m.ctcp_reply Irc::Bot::SOURCE_URL
211 # @bot.irclog "@ #{m.source} asked #{who} about source info"
215 def handle_help(m, params)
216 m.reply help(params[:topic])
219 def help(plugin, topic="")
224 _("config list => list configuration modules, config list <module> => list configuration keys for module <module>")
226 _("config get <key> => get configuration value for key <key>")
228 _("reset key <key> to the default")
230 _("config set <key> <value> => set configuration value for key <key> to <value>")
232 _("config desc <key> => describe what key <key> configures")
234 _("config add <value> to <key> => add value <value> to key <key> if <key> is an array")
236 _("config rm <value> from <key> => remove value <value> from key <key> if <key> is an array")
238 _("config module - bot configuration. usage: list, desc, get, set, unset, add, rm")
240 # "no help for config #{topic}"
243 _("nick <newnick> => change the bot nick to <newnick>, if possible")
245 _("status => display some information on the bot's status")
247 _("save => save current dynamic data and configuration")
249 _("rescan => reload modules and static facts")
251 _("version => describes software version")
253 _("config-related tasks: config, save, rescan, version, nick, status")
259 conf = ConfigModule.new
261 conf.map 'config list :module',
262 :action => 'handle_list',
263 :defaults => {:module => false},
265 # TODO this one is presently a security risk, since the bot
266 # stores the master password in the config. Do we need auth levels
267 # on the Bot::Config keys too?
268 conf.map 'config get :key',
269 :action => 'handle_get',
271 conf.map 'config desc :key',
272 :action => 'handle_desc',
274 conf.map 'config describe :key',
275 :action => 'handle_desc',
276 :auth_path => 'show::desc!'
277 conf.map 'config search *rx',
278 :action => 'handle_search',
282 :action => 'bot_save'
284 :action => 'bot_rescan'
285 conf.map "nick :nick",
286 :action => 'bot_nick'
288 :action => 'bot_status',
289 :auth_path => 'show::status'
292 # conf.map "registry stats",
293 # :action => 'bot_reg_stat',
294 # :auth_path => '!config::status'
296 :action => 'bot_version',
297 :auth_path => 'show::status'
299 conf.map 'config set :key *value',
300 :action => 'handle_set',
302 conf.map 'config add :value to :key',
303 :action => 'handle_add',
305 conf.map 'config rm :value from :key',
306 :action => 'handle_rm',
308 conf.map 'config del :value from :key',
309 :action => 'handle_rm',
311 conf.map 'config delete :value from :key',
312 :action => 'handle_rm',
314 conf.map 'config unset :key',
315 :action => 'handle_unset',
317 conf.map 'config reset :key',
318 :action => 'handle_unset',
321 conf.map 'config help :topic',
322 :action => 'handle_help',
323 :defaults => {:topic => false},
324 :auth_path => '!help!'
326 conf.default_auth('*', false)
327 conf.default_auth('show', true)
328 conf.default_auth('show::get', false)
329 # TODO these shouldn't be set here, we need a way to let the default
330 # permission be specified together with the ConfigValue
331 conf.default_auth('key', true)
332 conf.default_auth('key::auth::password', false)