]> git.netwichtig.de Git - user/henk/code/ruby/rbot.git/blob - lib/rbot/core/config.rb
203cf1de20faf1e21d87a21b53ada4b01f4d22c7
[user/henk/code/ruby/rbot.git] / lib / rbot / core / config.rb
1 #-- vim:sw=2:et
2 #++
3 #
4 # :title: rbot config management from IRC
5 #
6 # Author:: Giuseppe "Oblomov" Bilotta <giuseppe.bilotta@gmail.com>
7 # Copyright:: (C) 2006,2007 Giuseppe Bilotta
8 # License:: GPL v2
9
10 class ConfigModule < CoreBotModule
11
12   def version_string
13     if $version_timestamp.to_i > 0
14       ago = String.new ' ['
15       ago << Utils.secs_to_string(Time.now.to_i - $version_timestamp.to_i)
16       ago << ' ago]'
17     else
18       ago = ''
19     end
20     _("I'm a v. %{version}%{ago} rubybot%{copyright}%{url}") % {
21       :version => $version,
22       :ago => ago,
23       :copyright => ", #{Irc::Bot::COPYRIGHT_NOTICE}",
24       :url => " - #{Irc::Bot::SOURCE_URL}"
25     }
26   end
27
28   def save
29     @bot.config.save
30   end
31
32   def handle_list(m, params)
33     modules = []
34     if params[:module]
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)
39       end
40       if modules.empty?
41         m.reply _("no such module %{module}") % {:module => params[:module]}
42       else
43         m.reply modules.join(", ")
44       end
45     else
46       @bot.config.items.each_key do |key|
47         name = key.to_s.split('.').first
48         modules.push name unless modules.include?(name)
49       end
50       m.reply "modules: " + modules.join(", ")
51     end
52   end
53
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}
58       return
59     end
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}"
63   end
64
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}
69     end
70     m.reply "#{key}: #{@bot.config.items[key].desc}"
71   end
72
73   def handle_search(m, params)
74     rx = Regexp.new(params[:rx].to_s, true)
75     cfs = []
76     @bot.config.items.each do |k, v|
77       cfs << v if k.to_s.match(rx) or (v.desc.match(rx) rescue false)
78     end
79     if cfs.empty?
80       m.reply _("no config key found matching %{r}") % { :r => params[:rx].to_s}
81     else
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")
84     end
85   end
86
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}
91     end
92     return if !@bot.auth.allow?(@bot.config.items[key].auth_path, m.source, m.replyto)
93     @bot.config.items[key].unset
94     handle_get(m, params)
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
97   end
98
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]
104       return false
105     end
106     return false if !@bot.auth.allow?(@bot.config.items[key].auth_path, m.source, m.replyto)
107     begin
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]
111       return false
112     end
113     if @bot.config.items[key].requires_restart
114       m.reply _("this config change will take effect on the next restart") unless params[:silent]
115       return :restart
116     elsif @bot.config.items[key].requires_rescan
117       m.reply _("this config change will take effect on the next rescan") unless params[:silent]
118       return :rescan
119     else
120       m.okay unless params[:silent]
121       return true
122     end
123   end
124
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}
130       return
131     end
132     unless @bot.config.items[key].kind_of?(Config::ArrayValue)
133       m.reply _("config key %{key} is not an array") % {:key => key}
134       return
135     end
136     return if !@bot.auth.allow?(@bot.config.items[key].auth_path, m.source, m.replyto)
137     begin
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}
141       return
142     end
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
146   end
147
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}
153       return
154     end
155     unless @bot.config.items[key].kind_of?(Config::ArrayValue)
156       m.reply _("config key %{key} is not an array") % {:key => key}
157       return
158     end
159     return if !@bot.auth.allow?(@bot.config.items[key].auth_path, m.source, m.replyto)
160     begin
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}
164       return
165     end
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
169   end
170
171   def bot_save(m, param)
172     @bot.save
173     m.okay
174   end
175
176   def bot_rescan(m, param)
177     m.reply _("saving ...")
178     @bot.save
179     m.reply _("rescanning ...")
180     @bot.rescan
181     m.reply _("done. %{plugin_status}") % {:plugin_status => @bot.plugins.status(true)}
182   end
183
184   def bot_nick(m, param)
185     @bot.nickchg(param[:nick])
186   end
187
188   def bot_status(m, param)
189     m.reply @bot.status
190   end
191
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?
194   #
195   #  def bot_reg_stat(m, param)
196   #    m.reply @registry.stat.inspect
197   #  end
198
199   def bot_version(m, param)
200     m.reply version_string
201   end
202
203   def ctcp_listen(m)
204     who = m.private? ? "me" : m.target
205     case m.ctcp.intern
206     when :VERSION
207       m.ctcp_reply version_string
208       @bot.irclog "@ #{m.source} asked #{who} about version info"
209     when :SOURCE
210       m.ctcp_reply Irc::Bot::SOURCE_URL
211       @bot.irclog "@ #{m.source} asked #{who} about source info"
212     end
213   end
214
215   def handle_help(m, params)
216     m.reply help(params[:topic])
217   end
218
219   def help(plugin, topic="")
220     case plugin
221     when "config"
222       case topic
223       when "list"
224       _("config list => list configuration modules, config list <module> => list configuration keys for module <module>")
225       when "get"
226       _("config get <key> => get configuration value for key <key>")
227       when "unset"
228       _("reset key <key> to the default")
229       when "set"
230       _("config set <key> <value> => set configuration value for key <key> to <value>")
231       when "desc"
232       _("config desc <key> => describe what key <key> configures")
233       when "add"
234       _("config add <value> to <key> => add value <value> to key <key> if <key> is an array")
235       when "rm"
236       _("config rm <value> from <key> => remove value <value> from key <key> if <key> is an array")
237       else
238       _("config module - bot configuration. usage: list, desc, get, set, unset, add, rm")
239       # else
240       #   "no help for config #{topic}"
241       end
242     when "nick"
243       _("nick <newnick> => change the bot nick to <newnick>, if possible")
244     when "status"
245       _("status => display some information on the bot's status")
246     when "save"
247       _("save => save current dynamic data and configuration")
248     when "rescan"
249       _("rescan => reload modules and static facts")
250     when "version"
251       _("version => describes software version")
252     else
253       _("config-related tasks: config, save, rescan, version, nick, status")
254     end
255   end
256
257 end
258
259 conf = ConfigModule.new
260
261 conf.map 'config list :module',
262   :action => 'handle_list',
263   :defaults => {:module => false},
264   :auth_path => 'show'
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',
270   :auth_path => 'show'
271 conf.map 'config desc :key',
272   :action => 'handle_desc',
273   :auth_path => 'show'
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',
279   :auth_path => 'show'
280
281 conf.map "save",
282   :action => 'bot_save'
283 conf.map "rescan",
284   :action => 'bot_rescan'
285 conf.map "nick :nick",
286   :action => 'bot_nick'
287 conf.map "status",
288   :action => 'bot_status',
289   :auth_path => 'show::status'
290 # TODO see above
291 #
292 # conf.map "registry stats",
293 #   :action => 'bot_reg_stat',
294 #   :auth_path => '!config::status'
295 conf.map "version",
296   :action => 'bot_version',
297   :auth_path => 'show::status'
298
299 conf.map 'config set :key *value',
300   :action => 'handle_set',
301   :auth_path => 'edit'
302 conf.map 'config add :value to :key',
303   :action => 'handle_add',
304   :auth_path => 'edit'
305 conf.map 'config rm :value from :key',
306   :action => 'handle_rm',
307   :auth_path => 'edit'
308 conf.map 'config del :value from :key',
309   :action => 'handle_rm',
310   :auth_path => 'edit'
311 conf.map 'config delete :value from :key',
312   :action => 'handle_rm',
313   :auth_path => 'edit'
314 conf.map 'config unset :key',
315   :action => 'handle_unset',
316   :auth_path => 'edit'
317 conf.map 'config reset :key',
318   :action => 'handle_unset',
319   :auth_path => 'edit'
320
321 conf.map 'config help :topic',
322   :action => 'handle_help',
323   :defaults => {:topic => false},
324   :auth_path => '!help!'
325
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)
333