nadoka/nadoka

View on GitHub
plugins/modemanager.nb

Summary

Maintainability
Test Coverage
# -*-ruby-*-
#
# Copyright (c) 2004-2005 SASADA Koichi <ko1 at atdot.net>
#
# This program is free software with ABSOLUTELY NO WARRANTY.
# You can re-distribute and/or modify this program under
# the same terms of the Ruby's license.
#
#
# $Id$
#


=begin

== Abstract

Mode management bot

== Configuration

=== nadokarc

BotConfig => [
{
  :name => :ModeManager,
  #
  # You can specify modes setting in file or
  # String directly.
  #
  :file  => 'modes',
  # or :modes_file => 'modes',
  
  :modes => <<-__EOS__,
  [all]
  +o=*.jp
  __EOS__
  # wait for setting modes (by second, avarage)
  :wait => 5,
}
]

=== mode setting format

[<channel name> or 'all']
<+-><mode> pattern
<+-><mode> /regexp pattern/

<mode>: 'b' or 'v' or 'o'
<+->: '+' means add mode, '-' means remove mode

[example] -------------------------------
[all]
+o=*.jp
+o=/.*\.net/

[#nadoka]
+o=kiyoya* unak*
-o=ko1*
-----------------------------------------


=end

class ModeManager < Nadoka::NDK_Bot
  
  class Fnmexp
    def initialize str
      @str = str
    end

    def =~ x
      File.fnmatch @str, x, File::FNM_CASEFOLD
    end
  end

  def parse_setting setting
    s = nil
    setting.each_line{|l|
      l.strip!
      if /^\[(all|[\#\&\!\+].+)\]/ =~ l
        s = @config.identical_channel_name($1)
      elsif /^\[%(.+)\]/ =~ l   # for compatibility with madoka
        s = @config.identical_channel_name("\##{$1}:*.jp")
      elsif s && l.sub!(/^([+-][bvo]+)\s*=\s*/, '')
        mode = $1
        l.scan(/\S+/){|prefix|
          if %r!^/(.*)\/$! =~ prefix
            user = [mode, Regexp.new($1, Regexp::IGNORECASE)]
          else
            user = [mode, Fnmexp.new(prefix)]
          end
          @userlist[s] << user
        }
      end
    }
  end

  def bot_initialize
    @userlist = Hash.new{|h, k| h[k] = []}
    if file = (@bot_config[:modes_file] || @bot_config[:file])
      begin
        parse_setting File.read(file)
      rescue Exception => e
        "operator manager: #{e.class}(#{e.message})"
      end
    end
    if setting = @bot_config[:modes]
      parse_setting setting
    end
    @wait = @bot_config[:wait].to_i
    @wait = 3 if @wait <= 0
  end

  def search_mode_in_list list, prefix
    list.each{|e|
      if e[1] =~ prefix
        return e[0]
      end
    }
    nil
  end

  def search_mode ch, prefix
    search_mode_in_list(@userlist['all'], prefix) ||
    search_mode_in_list(@userlist[ch], prefix)
  end

  def on_join prefix, rch
    ch = @config.canonical_channel_name(rch)
    Thread.new{
      sleep(rand(@wait * 20) / 10.0)
      if prefix.nick != @state.nick &&
        (/o/ =~ @state.channel_user_mode(ch, @state.nick))
        if mode = search_mode(ch, prefix.to_s)
          current_modes = @state.channel_user_mode(ch, prefix.nick).split(//)
          if /^\+/ =~ mode
            noneed = mode.split(//)[1..-1].all?{|c|  current_modes.include?(c)}
          else
            noneed = mode.split(//)[1..-1].all?{|c| !current_modes.include?(c)}
          end

          change_mode(rch, mode, prefix.nick) unless noneed
        end
      end
    }
  end
end