]> git.netwichtig.de Git - user/henk/code/ruby/rbot.git/blob - data/rbot/plugins/bans.rb
plugin(search): fix wolfram and gdef, removed some
[user/henk/code/ruby/rbot.git] / data / rbot / plugins / bans.rb
1 #-- vim:sw=2:et
2 #++
3 #
4 # :title: Bans Plugin v3 for rbot 0.9.11 and later
5 #
6 # Author:: Marco Gulino <marco@kmobiletools.org>
7 # Author:: kamu <mr.kamu@gmail.com>
8 # Author:: Giuseppe Bilotta <giuseppe.bilotta@gmail.com>
9 #
10 # Copyright:: (C) 2006 Marco Gulino
11 # Copyright:: (C) 2007 kamu, Giuseppe Bilotta
12 #
13 # License:: GPL V2.
14 #
15 # Managing kick and bans, automatically removing bans after timeouts, quiet
16 # bans, and kickban/quietban based on regexp
17 #
18 # v1 -> v2 (kamu's version, never released)
19 #   * reworked
20 #   * autoactions triggered on join
21 #   * action on join or badword can be anything: kick, ban, kickban, quiet
22 #
23 # v2 -> v3 (GB)
24 #   * remove the 'bans' prefix from most of the commands
25 #   * (un)quiet has been renamed to (un)silence because 'quiet' was used to
26 #     tell the bot to keep quiet
27 #   * both (un)quiet and (un)silence are accepted as actions
28 #   * use the more descriptive 'onjoin' term for autoactions
29 #   * convert v1's (0.9.10) :bans and :bansmasks to BadWordActions and
30 #     WhitelistEntries
31 #   * enhanced list manipulation facilities
32 #   * fixed regexp usage in requirements for plugin map
33 #   * add proper auth management
34
35 define_structure :OnJoinAction, :host, :action, :channel, :reason
36 define_structure :BadWordAction, :regexp, :action, :channel, :timer, :reason
37 define_structure :WhitelistEntry, :host, :channel
38 define_structure :MassHlAction, :num, :perc, :action, :channel, :timer, :reason
39
40 class BansPlugin < Plugin
41
42   IdxRe = /^\d+$/
43   TimerRe = /^\d+[smhd]$/
44   ChannelRe = /^#+[^\s]+$/
45   ChannelAllRe = /^(?:all|#+[^\s]+)$/
46   ActionRe = /(?:ban|kick|kickban|silence|quiet)/
47
48   def name
49     "bans"
50   end
51
52   def make_badword_rx(txt)
53     return /\b(?:#{txt})\b/i
54   end
55
56   def initialize
57     super
58
59     # Convert old BadWordActions, which were simpler and labelled :bans
60     if @registry.has_key? :bans
61       badwords = Array.new
62       bans = @registry[:bans]
63       @registry[:bans].each { |ar|
64         case ar[0]
65         when "quietban"
66           action = :silence
67         when "kickban"
68           action = :kickban
69         else
70           # Shouldn't happen
71           warning "Unknown action in old data #{ar.inspect} -- entry ignored"
72           next
73         end
74         bans.delete(ar)
75         chan = ar[1].downcase
76         regexp = make_badword_rx(ar[2])
77         badwords << BadWordAction.new(regexp, action, chan, "0s", "")
78       }
79       @registry[:badwords] = badwords
80       if bans.length > 0
81         # Store the ones we couldn't convert
82         @registry[:bans] = bans
83       else
84         @registry.delete(:bans)
85       end
86     else
87       @registry[:badwords] = Array.new unless @registry.has_key? :badwords
88     end
89
90     # Convert old WhitelistEntries, which were simpler and labelled :bansmasks
91     if @registry.has_key? :bans
92       wl = Array.new
93       @registry[:bansmasks].each { |mask|
94         badwords << WhitelistEntry.new(mask, "all")
95       }
96       @registry[:whitelist] = wl
97       @registry.delete(:bansmasks)
98     else
99       @registry[:whitelist] = Array.new unless @registry.has_key? :whitelist
100     end
101
102     @registry[:onjoin] = Array.new unless @registry.has_key? :onjoin
103     @registry[:masshl] = Array.new unless @registry.has_key? :masshl
104   end
105
106   def help(plugin, topic="")
107     case plugin
108     when "ban"
109       return "ban <nick/hostmask> [Xs/m/h/d] [#channel]: ban a user from the given channel for the given amount of time. default is forever, on the current channel"
110     when "unban"
111       return "unban <nick/hostmask> [#channel]: unban a user from the given channel. defaults to the current channel"
112     when "kick"
113       return "kick <nick> [#channel] [reason ...]: kick a user from the given channel with the given reason. defaults to the current channel, no reason"
114     when "kickban"
115       return "kickban <nick> [Xs/m/h/d] [#channel] [reason ...]: kicks and bans a user from the given channel for the given amount of time, with the given reason. default is forever, on the current channel, with no reason"
116     when "silence"
117       return "silence <nick/hostmask> [Xs/m/h/d] [#channel]: silence a user on the given channel for the given time. default is forever, on the current channel. not all servers support silencing users"
118     when "unsilence"
119       return "unsilence <nick/hostmask> [#channel]: allow the given user to talk on the given channel. defaults to the current channel"
120     when "bans"
121       case topic
122       when "add"
123         return "bans add <onjoin|badword|whitelist|masshl>: add an automatic action for people that join or say some bad word, or a whitelist entry. further help available"
124       when "add onjoin"
125         return "bans add onjoin <hostmask> [action] [#channel] [reason ...]: will add an autoaction for any one who joins with hostmask. default action is silence, default channel is all"
126       when "add badword"
127         return "bans add badword <regexp> [action] [Xs/m/h/d] [#channel|all] [reason ...]: adds a badword regexp, if a user sends a message that matches regexp, the action will be invoked. default action is silence, default channel is all"
128       when "add whitelist"
129         return "bans add whitelist <hostmask> [#channel|all]: add the given hostmask to the whitelist. no autoaction will be triggered by users on the whitelist"
130       when "add masshl"
131         return "masshl add <max_nicks|percentage> [action] [Xs/m/h/d] [#channel|all] [reason ...]: adds an massive highligh action. You can use both max and % in one trigger, the higher value will be taken. For two triggers in one channel, the one with higher requirements will be taken"
132       when "rm"
133         return "bans rm <onjoin|badword|whitelist> <hostmask/regexp> [#channel], or bans rm <onjoin|badword|whitelist> index <num>: removes the specified onjoin or badword rule or whitelist entry. For masshl, bans rm masshl index [#channel|all]"
134       when "list"
135         return"bans list <onjoin|badword|whitelist|masshl>: lists all onjoin or badwords or whitelist entries. For masshl, you can add [#channel|all]"
136       else
137         return "commands are: add, add onjoin, add badword, add whitelist, add masshl, rm, list"
138       end
139     end
140     return "bans <command>: allows a user of the bot to do a range of bans and unbans. commands are: [un]ban, kick[ban], [un]silence, add, rm and list"
141   end
142
143   def message(m)
144     return unless m.channel
145
146     # check the whitelist first
147     @registry[:whitelist].each { |white|
148       next unless ['all', m.target.downcase].include?(white.channel)
149       return if m.source.matches?(white.host)
150     }
151
152     # check the badwords next
153     @registry[:badwords].each { |badword|
154       next unless ['all', m.target.downcase].include?(badword.channel)
155       next unless badword.regexp.match(m.plainmessage)
156
157       m.reply "bad word detected! #{badword.action} for #{badword.timer} because: #{badword.reason}"
158       do_cmd(badword.action.to_sym, m.source.nick, m.target, badword.timer, badword.reason)
159       return
160     }
161
162     # and finally, see if the user triggered masshl
163     mm = m.plainmessage.irc_downcase(m.server.casemap).split(/[\s\.,:]/)
164     nicks_said = (m.channel.users.map { |u| u.downcase} & mm).size
165     return unless nicks_said > 0 # not really needed, but saves some cycles
166     got_nicks = 0
167     masshl_action = nil
168     @registry[:masshl].each { |masshl|
169       next unless masshl.channel == m.channel.downcase or masshl.channel == "all"
170       needed = [masshl.num.to_i, (masshl.perc * m.channel.user_nicks.size / 100).to_i].max
171       next if needed > nicks_said or needed < got_nicks
172       masshl_action = masshl
173       got_nicks = needed
174     }
175     return unless masshl_action
176     do_cmd masshl_action.action.intern, m.sourcenick, m.channel, masshl_action.timer, masshl_action.reason
177   end
178
179   def join(m)
180     @registry[:whitelist].each { |white|
181       next unless ['all', m.target.downcase].include?(white.channel)
182       return if m.source.matches?(white.host)
183     }
184
185     @registry[:onjoin].each { |auto|
186       next unless ['all', m.target.downcase].include?(auto.channel)
187       next unless m.source.matches? auto.host
188
189       do_cmd(auto.action.to_sym, m.source.nick, m.target, "0s", auto.reason)
190       return
191     }
192   end
193
194   def ban_user(m, params=nil)
195     nick, channel = params[:nick], check_channel(m, params[:channel])
196     timer = params[:timer]
197     do_cmd(:ban, nick, channel, timer)
198   end
199
200   def unban_user(m, params=nil)
201     nick, channel = params[:nick], check_channel(m, params[:channel])
202     do_cmd(:unban, nick, channel)
203   end
204
205   def kick_user(m, params=nil)
206     nick, channel = params[:nick], check_channel(m, params[:channel])
207     reason = params[:reason].to_s
208     do_cmd(:kick, nick, channel, "0s", reason)
209   end
210
211   def kickban_user(m, params=nil)
212     nick, channel, reason = params[:nick], check_channel(m, params[:channel])
213     timer, reason = params[:timer], params[:reason].to_s
214     do_cmd(:kickban, nick, channel, timer, reason)
215   end
216
217   def silence_user(m, params=nil)
218     nick, channel = params[:nick], check_channel(m, params[:channel])
219     timer = params[:timer]
220     do_cmd(:silence, nick, channel, timer)
221   end
222
223   def unsilence_user(m, params=nil)
224     nick, channel = params[:nick], check_channel(m, params[:channel])
225     do_cmd(:unsilence, nick, channel)
226   end
227
228   def add_masshl(m, params=nil)
229     num = params[:num].to_i
230     perc = params[:perc] ? /(\d{1,2})\%/.match(params[:perc])[1].to_i : 0
231     channel, action = params[:channel].downcase.dup, params[:action]
232     timer, reason = params[:timer].dup, params[:reason].to_s
233     if perc == 0 and num == 0
234       m.reply "both triggers 0, you don't want this."
235       return
236     end
237
238     masshl = @registry[:masshl]
239     masshl << MassHlAction.new(num, perc, action, channel, timer, reason)
240     @registry[:masshl] = masshl
241
242     m.okay
243   end
244
245   def rm_masshl(m, params=nil)
246     masshl = @registry[:masshl]
247     masshl_w = params[:channel] ? masshl.select { |mh| mh.channel == params[:channel].downcase } : masshl
248     count = masshl_w.length
249     idx = params[:idx].to_i
250
251     if idx > count
252       m.reply "No such masshl \##{idx}"
253       return
254     end
255     masshl.delete(masshl_w[idx-1])
256     @registry[:masshl] = masshl
257     m.okay
258   end
259
260   def list_masshl(m, params=nil)
261     masshl = @registry[:masshl]
262     masshl = masshl.select { |mh| mh.channel == params[:channel].downcase } if params[:channel]
263     m.reply params[:channel] ? "masshl rules: #{masshl.length} for #{params[:channel]}" : "masshl rules: #{masshl.length}"
264     masshl.each_with_index { |mh, idx|
265       m.reply "\##{idx+1}: #{mh.num} | #{mh.perc}% | #{mh.action} | #{mh.channel} | #{mh.timer} | #{mh.reason}"
266     }
267   end
268
269   def add_onjoin(m, params=nil)
270     begin
271       host, channel = m.server.new_netmask(params[:host]), params[:channel].downcase
272       action, reason = params[:action], params[:reason].to_s
273
274       autos = @registry[:onjoin]
275       autos << OnJoinAction.new(host, action, channel, reason.dup)
276       @registry[:onjoin] = autos
277
278       m.okay
279     rescue
280       error $!
281       m.reply $!
282     end
283   end
284
285   def list_onjoin(m, params=nil)
286     m.reply "onjoin rules: #{@registry[:onjoin].length}"
287     @registry[:onjoin].each_with_index { |auto, idx|
288       m.reply "\##{idx+1}: #{auto.host} | #{auto.action} | #{auto.channel} | '#{auto.reason}'"
289     }
290   end
291
292   def rm_onjoin(m, params=nil)
293     autos = @registry[:onjoin]
294     count = autos.length
295
296     idx = nil
297     idx = params[:idx].to_i if params[:idx]
298
299     if idx
300       if idx > count
301         m.reply "No such onjoin \##{idx}"
302         return
303       end
304       autos.delete_at(idx-1)
305     else
306       begin
307         host = m.server.new_netmask(params[:host])
308         channel = params[:channel].downcase
309
310         autos.each { |rule|
311           next unless ['all', rule.channel].include?(channel)
312           autos.delete rule if rule.host == host
313         }
314       rescue
315         error $!
316         m.reply $!
317       end
318     end
319     @registry[:onjoin] = autos
320     if count > autos.length
321       m.okay
322     else
323       m.reply "No matching onjoin rule for #{host} found"
324     end
325   end
326
327   def add_badword(m, params=nil)
328     regexp, channel = make_badword_rx(params[:regexp]), params[:channel].downcase.dup
329     action, timer, reason = params[:action], params[:timer].dup, params[:reason].to_s
330
331     badwords = @registry[:badwords]
332     badwords << BadWordAction.new(regexp, action, channel, timer, reason)
333     @registry[:badwords] = badwords
334
335     m.okay
336   end
337
338   def list_badword(m, params=nil)
339     m.reply "badword rules: #{@registry[:badwords].length}"
340
341     @registry[:badwords].each_with_index { |badword, idx|
342       m.reply "\##{idx+1}: #{badword.regexp.source} | #{badword.action} | #{badword.channel} | #{badword.timer} | #{badword.reason}"
343     }
344   end
345
346   def rm_badword(m, params=nil)
347     badwords = @registry[:badwords]
348     count = badwords.length
349
350     idx = nil
351     idx = params[:idx].to_i if params[:idx]
352
353     if idx
354       if idx > count
355         m.reply "No such badword \##{idx}"
356         return
357       end
358       badwords.delete_at(idx-1)
359     else
360       channel = params[:channel].downcase
361
362       regexp = make_badword_rx(params[:regexp])
363       debug "Trying to remove #{regexp.inspect} from #{badwords.inspect}"
364
365       badwords.each { |badword|
366         next unless ['all', badword.channel].include?(channel)
367         debug "Removing #{badword.inspect}" if badword.regexp == regexp
368         badwords.delete(badword) if badword.regexp == regexp
369       }
370     end
371
372     @registry[:badwords] = badwords
373     if count > badwords.length
374       m.okay
375     else
376       m.reply "No matching badword #{regexp} found"
377     end
378   end
379
380   def add_whitelist(m, params=nil)
381     begin
382       host, channel = m.server.new_netmask(params[:host]), params[:channel].downcase
383
384       # TODO check if a whitelist entry for this host already exists
385       whitelist = @registry[:whitelist]
386       whitelist << WhitelistEntry.new(host, channel)
387       @registry[:whitelist] = whitelist
388
389       m.okay
390     rescue
391       error $!
392       m.reply $!
393     end
394   end
395
396   def list_whitelist(m, params=nil)
397     m.reply "whitelist entries: #{@registry[:whitelist].length}"
398     @registry[:whitelist].each_with_index { |auto, idx|
399       m.reply "\##{idx+1}: #{auto.host} | #{auto.channel}"
400     }
401   end
402
403   def rm_whitelist(m, params=nil)
404     wl = @registry[:whitelist]
405     count = wl.length
406
407     idx = nil
408     idx = params[:idx].to_i if params[:idx]
409
410     if idx
411       if idx > count
412         m.reply "No such whitelist entry \##{idx}"
413         return
414       end
415       wl.delete_at(idx-1)
416     else
417       begin
418         host = m.server.new_netmask(params[:host])
419         channel = params[:channel].downcase
420
421         wl.each { |rule|
422           next unless ['all', rule.channel].include?(channel)
423           wl.delete rule if rule.host == host
424         }
425       rescue
426         error $!
427         m.reply $!
428       end
429     end
430     @registry[:whitelist] = wl
431     if count > whitelist.length
432       m.okay
433     else
434       m.reply "No host matching #{host}"
435     end
436   end
437
438   private
439   def check_channel(m, strchannel)
440     begin
441       raise "must specify channel if using privmsg" if m.private? and not strchannel
442       channel = m.server.channel(strchannel) || m.target
443       raise "I am not in that channel" unless channel.has_user?(@bot.nick)
444
445       return channel
446     rescue
447       error $!
448       m.reply $!
449     end
450   end
451
452   def do_cmd(action, nick, channel, timer_in=nil, reason=nil)
453     case timer_in
454     when nil
455       timer = 0
456     when /^(\d+)s$/
457       timer = $1.to_i
458     when /^(\d+)m$/
459       timer = $1.to_i * 60
460     when /^(\d+)h$/
461       timer = $1.to_i * 60 * 60
462     when /^(\d+)d$/
463       timer = $1.to_i * 60 * 60 * 24
464     else
465       raise "Wrong time specifications"
466     end
467
468     case action
469     when :ban
470       set_temporary_mode(channel, 'b', nick, timer)
471     when :unban
472       set_mode(channel, "-b", nick)
473     when :kick
474       do_kick(channel, nick, reason)
475     when :kickban
476       set_temporary_mode(channel, 'b', nick, timer)
477       do_kick(channel, nick, reason)
478     when :silence, :quiet
479       set_mode(channel, "+q", nick)
480       @bot.timer.add_once(timer) { set_mode(channel, "-q", nick) } if timer > 0
481     when :unsilence, :unquiet
482       set_mode(channel, "-q", nick)
483     end
484   end
485
486   def set_mode(channel, mode, nick)
487     host = channel.has_user?(nick) ? "*!*@" + channel.get_user(nick).host : nick
488     @bot.mode(channel, mode, host)
489   end
490
491   def set_temporary_mode(channel, mode, nick, timer)
492     host = channel.has_user?(nick) ? "*!*@" + channel.users[nick].host : nick
493     @bot.mode(channel, "+#{mode}", host)
494     return if timer == 0
495     @bot.timer.add_once(timer) { @bot.mode(channel, "-#{mode}", host) }
496   end
497
498   def do_kick(channel, nick, reason="")
499     @bot.kick(channel, nick, reason)
500   end
501 end
502
503 plugin = BansPlugin.new
504
505 plugin.default_auth( 'act', false )
506 plugin.default_auth( 'edit', false )
507 plugin.default_auth( 'list', true )
508
509 plugin.map 'ban :nick :timer :channel', :action => 'ban_user',
510   :requirements => {:timer => BansPlugin::TimerRe, :channel => BansPlugin::ChannelRe},
511   :defaults => {:timer => nil, :channel => nil},
512   :auth_path => 'act'
513 plugin.map 'unban :nick :channel', :action => 'unban_user',
514   :requirements => {:channel => BansPlugin::ChannelRe},
515   :defaults => {:channel => nil},
516   :auth_path => 'act'
517 plugin.map 'kick :nick :channel *reason', :action => 'kick_user',
518   :requirements => {:channel => BansPlugin::ChannelRe},
519   :defaults => {:channel => nil, :reason => 'requested'},
520   :auth_path => 'act'
521 plugin.map 'kickban :nick :timer :channel *reason', :action => 'kickban_user',
522   :requirements => {:timer => BansPlugin::TimerRe, :channel => BansPlugin::ChannelRe},
523   :defaults => {:timer => nil, :channel => nil, :reason => 'requested'},
524   :auth_path => 'act'
525 plugin.map 'silence :nick :timer :channel', :action => 'silence_user',
526   :requirements => {:timer => BansPlugin::TimerRe, :channel => BansPlugin::ChannelRe},
527   :defaults => {:timer => nil, :channel => nil},
528   :auth_path => 'act'
529 plugin.map 'unsilence :nick :channel', :action => 'unsilence_user',
530   :requirements => {:channel => BansPlugin::ChannelRe},
531   :defaults => {:channel => nil},
532   :auth_path => 'act'
533
534 plugin.map 'bans add onjoin :host :action :channel *reason', :action => 'add_onjoin',
535   :requirements => {:action => BansPlugin::ActionRe, :channel => BansPlugin::ChannelAllRe},
536   :defaults => {:action => 'kickban', :channel => 'all', :reason => 'netmask not welcome'},
537   :auth_path => 'edit::onjoin'
538 plugin.map 'bans rm onjoin index :idx', :action => 'rm_onjoin',
539   :requirements => {:num => BansPlugin::IdxRe},
540   :auth_path => 'edit::onjoin'
541 plugin.map 'bans rm onjoin :host :channel', :action => 'rm_onjoin',
542   :requirements => {:channel => BansPlugin::ChannelAllRe},
543   :defaults => {:channel => 'all'},
544   :auth_path => 'edit::onjoin'
545 plugin.map 'bans list onjoin[s]', :action => 'list_onjoin',
546   :auth_path => 'list::onjoin'
547
548 plugin.map 'bans add badword :regexp :action :timer :channel *reason', :action => 'add_badword',
549   :requirements => {:action => BansPlugin::ActionRe, :timer => BansPlugin::TimerRe, :channel => BansPlugin::ChannelAllRe},
550   :defaults => {:action => 'silence', :timer => "0s", :channel => 'all', :reason => 'bad word'},
551   :auth_path => 'edit::badword'
552 plugin.map 'bans rm badword index :idx', :action => 'rm_badword',
553   :requirements => {:num => BansPlugin::IdxRe},
554   :auth_path => 'edit::badword'
555 plugin.map 'bans rm badword :regexp :channel', :action => 'rm_badword',
556   :requirements => {:channel => BansPlugin::ChannelAllRe},
557   :defaults => {:channel => 'all'},
558   :auth_path => 'edit::badword'
559 plugin.map 'bans list badword[s]', :action => 'list_badword',
560   :auth_path => 'list::badword'
561
562 plugin.map 'bans add whitelist :host :channel', :action => 'add_whitelist',
563   :requirements => {:channel => BansPlugin::ChannelAllRe},
564   :defaults => {:channel => 'all'},
565   :auth_path => 'edit::whitelist'
566 plugin.map 'bans rm whitelist index :idx', :action => 'rm_whitelist',
567   :requirements => {:num => BansPlugin::IdxRe},
568   :auth_path => 'edit::whitelist'
569 plugin.map 'bans rm whitelist :host :channel', :action => 'rm_whitelist',
570   :requirements => {:channel => BansPlugin::ChannelAllRe},
571   :defaults => {:channel => 'all'},
572   :auth_path => 'edit::whitelist'
573 plugin.map 'bans list whitelist', :action => 'list_whitelist',
574   :auth_path => 'list::whitelist'
575
576 plugin.map 'bans add masshl :num :perc :action :timer :channel *reason', :action => 'add_masshl',
577   :requirements => {:num => /\d{1,2}/, :perc => /\d{1,2}\%/,:action => BansPlugin::ActionRe, :timer => BansPlugin::TimerRe, :channel => BansPlugin::ChannelAllRe},
578   :defaults => {:action => 'silence', :timer => "0s", :channel => 'all', :reason => 'masshl'},
579   :auth_path => 'edit::masshl'
580 plugin.map 'bans add masshl :perc :num :action :timer :channel *reason', :action => 'add_masshl',
581   :requirements => {:num => /\d{1,2}/, :perc => /\d{1,2}\%/,:action => BansPlugin::ActionRe, :timer => BansPlugin::TimerRe, :channel => BansPlugin::ChannelAllRe},
582   :defaults => {:action => 'silence', :timer => "0s", :channel => 'all', :reason => 'masshl'},
583   :auth_path => 'edit::masshl'
584 plugin.map 'bans add masshl :perc :action :timer :channel *reason', :action => 'add_masshl',
585   :requirements => {:num => /\d{1,2}/, :perc => /\d{1,2}\%/,:action => BansPlugin::ActionRe, :timer => BansPlugin::TimerRe, :channel => BansPlugin::ChannelAllRe},
586   :defaults => {:num => 0, :action => 'silence', :timer => "0s", :channel => 'all', :reason => 'masshl'},
587   :auth_path => 'edit::masshl'
588 plugin.map 'bans add masshl :num :action :timer :channel *reason', :action => 'add_masshl',
589   :requirements => {:num => /\d{1,2}/, :perc => /\d{1,2}\%/,:action => BansPlugin::ActionRe, :timer => BansPlugin::TimerRe, :channel => BansPlugin::ChannelAllRe},
590   :defaults => {:perc => "0%", :action => 'silence', :timer => "0s", :channel => 'all', :reason => 'masshl'},
591   :auth_path => 'edit::masshl'
592 plugin.map 'bans rm masshl :idx', :action => 'rm_masshl',
593   :requirements => {:channel => nil, :num => BansPlugin::IdxRe},
594   :auth_path => 'edit::masshl'
595 plugin.map 'bans rm masshl :idx :channel', :action => 'rm_masshl',
596   :requirements => {:channel => BansPlugin::ChannelAllRe},
597   :defaults => {:channel => nil},
598   :auth_path => 'edit::masshl'
599 plugin.map 'bans list masshl', :action => 'list_masshl',
600   :auth_path => 'list::masshl'
601 plugin.map 'bans list masshl :channel', :action => 'list_masshl',
602   :defaults => {:channel => nil},
603   :auth_path => 'list::masshl'