nadoka-0.7.6/0000755000000000000000000000000011640634705011465 5ustar rootrootnadoka-0.7.6/log/0000755000000000000000000000000011640634704012245 5ustar rootrootnadoka-0.7.6/ndk/0000755000000000000000000000000011640634705012241 5ustar rootrootnadoka-0.7.6/ndk/client.rb0000644000000000000000000001746610566613051014057 0ustar rootroot# # Copyright (c) 2004-2005 SASADA Koichi # # 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: client.rb 181 2007-02-20 15:39:21Z znz $ # Create : K.S. 04/04/17 16:50:10 # require 'thread' module Nadoka class NDK_Client def initialize config, sock, manager @sock = sock @config = config @logger = config.logger @manager= manager @state = manager.state @queue = Queue.new @remote_host = @sock.peeraddr[2] @thread = Thread.current @connected = false # client information @realname = nil @hostname = nil end attr_writer :logger attr_reader :remote_host def start send_thread = Thread.new{ begin while q = @queue.pop begin send_to_client q end end rescue Exception => e @manager.ndk_error e end } begin if login @connected = true begin @manager.invoke_event :leave_away, @manager.client_count @manager.invoke_event :invoke_bot, :client_login, @manager.client_count, self while msg = recv_from_client send_from_client msg, self end rescue NDK_QuitClient # finish ensure @manager.invoke_event :invoke_bot, :client_logout, @manager.client_count, self end end rescue Exception @manager.ndk_error $! ensure @logger.slog "Client #{@realname}@#{@remote_host} disconnected." @sock.close send_thread.kill if send_thread && send_thread.alive? end end def kill @thread && @thread.kill end def recv_from_client while !@sock.closed? begin str = @sock.gets if str msg = ::RICE::Message::parse str case msg.command when 'PING' send_msg Cmd.pong(*msg.params[0]), false when 'PONG' # ignore else @logger.dlog "[C>] #{str}" return msg end else break end rescue ::RICE::UnknownCommand, ::RICE::InvalidMessage @logger.slog "Invalid Message: #{str}" rescue Exception => e @manager.ndk_error e break end end end def push msg if @connected @queue << msg end end alias << push def login pass = nil nick = nil @username = nil while (nick == nil) || (@username == nil) msg = recv_from_client return nil if msg == nil case msg.command when 'USER' @username, @hostname, @servername, @realname = msg.params when 'NICK' nick = msg.params[0] when 'PASS' pass = msg.params[0] else raise "Illegal login sequence: #{msg}" end end if @config.client_server_pass && (@config.client_server_pass != pass) send_reply Rpl.err_passwdmismatch(nick, "Password Incorrect.") return false end send_reply Rpl.rpl_welcome( nick, 'Welcome to the Internet Relay Network '+"#{nick}!#{@username}@#{@remote_host}") send_reply Rpl.rpl_yourhost(nick, "Your host is nadoka, running version #{NDK_Version}") send_reply Rpl.rpl_created( nick, 'This server was created ' + NDK_Created.asctime) send_reply Rpl.rpl_myinfo( nick, "nadoka #{NDK_Version} aoOirw abeiIklmnoOpqrstv") send_motd(nick) send_command Cmd.nick(@state.nick), nick nick = @manager.state.nick @manager.state.current_channels.each{|ch, chs| send_command Cmd.join(chs.name) if chs.topic send_reply Rpl.rpl_topic(@state.nick, chs.name, chs.topic) else send_reply Rpl.rpl_notopic(@state.nick, chs.name, "No topic is set.") end send_reply Rpl.rpl_namreply( @state.nick, chs.state, chs.name, chs.names.join(' ')) send_reply Rpl.rpl_endofnames(@state.nick, chs.name, "End of NAMES list.") } @logger.slog "Client #{@realname}@#{@remote_host} connected." true end def send_motd nick send_reply Rpl.rpl_motdstart(nick, "- Nadoka Message of the Day - ") send_reply Rpl.rpl_motd(nick, "- Enjoy IRC chat with Nadoka chan!") send_reply Rpl.rpl_motd(nick, "- ") send_reply Rpl.rpl_endofmotd(nick, "End of MOTD command.") end # :who!~username@host CMD .. def send_command cmd, nick = @manager.state.nick msg = add_prefix(cmd, "#{nick}!#{@username}@#{@remote_host}") send_msg msg end # :serverinfo REPL ... def send_reply repl msg = add_prefix(repl, @config.nadoka_server_name) send_msg msg end def send_msg msg, logging=true @logger.dlog "[C<] #{msg}" if logging unless @sock.closed? begin @sock.write msg.to_s rescue Exception => e @manager.ndk_error e end end end def send_to_client msg if /^\d+/ =~ msg.command send_reply msg else send_msg msg end end def add_prefix cmd, prefix = "#{@manager.state.nick}!#{@username}@#{@remote_host}" cmd.prefix = prefix cmd end def add_prefix2 cmd, nick cmd.prefix = "#{nick}!#{@username}@#{@remote_host}" cmd end ::RICE::Command.regist_command('NADOKA') # client -> server handling def send_from_client msg, from until @manager.connected # ignore return end case msg.command when 'NADOKA' nadoka_client_command msg.params[0], msg.params[1..-1] return when 'QUIT' raise NDK_QuitClient when 'PRIVMSG', 'NOTICE' if /^\/nadoka/ =~ msg.params[1] _, cmd, *args = msg.params[1].split(/\s+/) nadoka_client_command cmd, args return end if @manager.send_to_bot(:client_privmsg, self, msg.params[0], msg.params[1]) @manager.send_to_server msg @manager.send_to_clients_otherwise msg, from end else @manager.send_to_server msg end end def client_notice msg self << Cmd.notice(@state.nick, msg) end def state "client from #{@remote_host}(#{@username}, #{@hostname}, #{@servername}, #{@realname})" end NdkCommandDescription = { # 'QUIT' => 'quite nadoka program', 'RESTART' => 'restart nadoka program(not relaod *.rb programs)', 'RELOAD' => 'reload configurations and bot programs(*.nb)', 'RECONNECT' => 'reconnect next server', 'STATUS' => 'show nadoka running status', } def nadoka_client_command cmd, args cmd ||= '' case cmd.upcase when 'QUIT' @manager.invoke_event :quit_program client_notice 'nadoka will be quit. bye!' when 'RESTART' @manager.invoke_event :restart_program, self client_notice 'nadoka will be restart. see you later.' when 'RELOAD' @manager.invoke_event :reload_config, self when 'RECONNECT' @manager.invoke_event :reconnect_to_server, self when 'STATUS' @manager.ndk_status.each{|msg| client_notice msg} when 'HELP' self << Cmd.notice(@state.nick, 'available: ' + NdkCommandDescription.keys.join(', ')) if args[1] self << Cmd.notice(@state.nick, "#{args[1]}: #{NdkCommandDescription[args[1].upcase]}") end else if @manager.send_to_bot :nadoka_command, self, cmd, *args self << Cmd.notice(@state.nick, 'No such command. Use /NADOKA HELP.') end end end end end nadoka-0.7.6/ndk/bot.rb0000644000000000000000000001220211501533602013335 0ustar rootroot# # Copyright (c) 2004-2005 SASADA Koichi # # 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 licence. # # # $Id: bot.rb 247 2010-12-14 00:17:06Z znz $ # Create : K.S. 04/04/19 00:39:48 # # # To make bot for nadoka, see this code. # module Nadoka class NDK_Bot # To initialize bot instance, please override this. def bot_initialize # do something end # This method will be called when reload configuration. def bot_destruct # do something end # override me def bot_state info = "#<#{self.class}: #{@bot_config.inspect}>" if info.length > 100 info[0..100] + '...' else info end end # To access bot configuration, please use this. # # in configuration file, # BotConfig = [ # :BotClassName1, # :BotClassName2, # { # :name => :BotClassName3, # :setX => X, # :setY => Y, # ... # }, # ] # # You can access above setting via @bot_config # def bot_init_utils bot_init_available_channel bot_init_same_bot end def bot_init_available_channel if @bot_config.key?(:channels) channels = '\A(?:' + @bot_config[:channels].collect{|ch| Regexp.quote(ch) }.join('|') + ')\z' @available_channel = Regexp.compile(channels) else @available_channel = @bot_config[:ch] || // end end def bot_init_same_bot @same_bot = @bot_config[:same_bot] || /(?!)/ end def same_bot?(ch) if @state.channel_users(ccn(ch)).find{|x| @same_bot =~ x } true else false end end def ccn2rcn ccn chs = @manager.state.current_channels[ccn] chs ? chs.name : ccn end # Mostly, you need this method. def send_notice ch, msg rch = ccn2rcn(ch) msg = Cmd.notice(rch, msg) @manager.send_to_server msg @manager.send_to_clients_otherwise msg, nil end # Usually, you must not use this def send_privmsg ch, msg rch = ccn2rcn(ch) msg = Cmd.privmsg(rch, msg) @manager.send_to_server msg @manager.send_to_clients_otherwise msg, nil end # Change user's mode as 'mode' on ch. def change_mode ch, mode, user rch = ccn2rcn(ch) send_msg Cmd.mode(rch, mode, user) end # Change your nick to 'nick'. def change_nick nick send_msg Cmd.nick(nick) end # Send command or reply(?) to server. def send_msg msg @manager.send_to_server msg end # ccn or canonical_channel_name def canonical_channel_name ch @config.canonical_channel_name ch end alias ccn canonical_channel_name =begin # ... # def on_[IRC Command or Reply(3 digits)] prefix(nick only), param1, param2, ... # # end # # like these def on_privmsg prefix, ch, msg end def on_join prefix, ch end def on_part prefix, ch, msg='' end def on_quit prefix, msg='' end def on_xxx prefix, *params end In above methods, you can access nick, user, host information via prefix argument variable like this. - prefix.nick - prefix.user - prefix.host ###### # special event # This method will be called when received every message def on_every_message prefix, command, *args # end # if 'nick' user quit client and part ch, this event is called. def on_quit_from_channel ch, nick, qmsg # do something end # It's special event that will be called about a minute. def on_timer timeobj # do something end # It's special event that will be called when new client join. def on_client_login client_count, client # do something end # It's special event that will be called when a client part. def on_client_logout client_count, client # do something end # undocumented def on_client_privmsg client, ch, msg # do something end # undocumented def on_nadoka_command client, command, *params # do something end # undocumented def on_server_connected # do something end # on signal 'sigusr[12]' trapped def on_sigusr[12] # no arguments # do something end You can access your current state on IRC server via @state. - @state.nick # your current nick - @state.channels # channels which you are join ['ch1', 'ch2', ...] # need canonicalized channel name - @state.channel_users(ch) # channel users ['user1', ...] - @state.channel_user_mode(ch, nick) =end Cmd = ::Nadoka::Cmd Rpl = ::Nadoka::Rpl def initialize manager, config, bot_config @manager = manager @config = config @logger = config.logger @state = manager.state @bot_config = bot_config bot_initialize end def config @bot_config end def self.inherited klass NDK_Config::BotClasses[klass.name.downcase.intern] = klass end end end nadoka-0.7.6/ndk/server_state.rb0000644000000000000000000001555510566613051015304 0ustar rootroot# # Copyright (c) 2004-2005 SASADA Koichi # # 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: server_state.rb 181 2007-02-20 15:39:21Z znz $ # Create : K.S. 04/04/20 10:42:27 # module Nadoka class NDK_State class ChannelState def initialize name @name = name @topic = nil @member= {} # member data # { "nick" => "mode", ... } end attr_accessor :topic attr_reader :member, :name # get members nick array def members @member.keys end # get user's mode def mode nick @member[nick] end def names @member.map{|nick, mode| prefix = if /o/ =~ mode '@' elsif /v/ =~ mode '+' else '' end prefix + nick } end def state '=' end def clear_members @member = {} end ##################################### def on_join nick, mode='' @member[nick] = mode end def on_part nick @member.delete nick end def on_nick nick, newnick if @member.has_key? nick @member[newnick] = @member[nick] @member.delete nick end end def on_kick nick if @member.has_key? nick @member.delete nick end end MODE_WITH_NICK_ARG = 'ov' MODE_WITH_ARGS = 'klbeI' MODE_WITHOUT_ARGS = 'aimnqpsrt' def on_mode nick, args while mode = args.shift modes = mode.split(//) flag = modes.shift modes.each{|m| if MODE_WITH_NICK_ARG.include? m chg_mode args.shift, flag, m elsif MODE_WITH_ARGS.include? m args.shift elsif MODE_WITHOUT_ARGS.include? m # ignore end } end end def chg_mode nick, flag, mode if @member.has_key? nick if flag == '+' @member[nick] += mode elsif flag == '-' @member[nick].gsub!(mode, '') end end end def to_s str = '' @member.each{|k, v| str << "#{k}: #{v}, " } str end end def initialize manager @manager = manager @config = nil @logger = nil @current_nick = nil @original_nick = nil @try_nick = nil @current_channels = {} end attr_reader :current_channels attr_reader :current_nick attr_accessor :original_nick attr_writer :logger, :config def nick=(n) @try_nick = nil @current_nick = n end def nick @current_nick end def nick_succ fail_nick if @try_nick if @try_nick.length == fail_nick @try_nick.succ! else @try_nick = fail_nick[0..-2] + '0' end else @try_nick = fail_nick + '0' end end def channels @current_channels.keys end def channel_raw_names @current_channels.map{|k, cs| cs.name} end # need canonicalized channel name def channel_users ch if @current_channels.has_key? ch @current_channels[ch].members else [] end end # need canonicalized channel name def channel_user_mode ch, user if channel_users(ch).include?(user) @current_channels[ch].mode(user) else '' end end def canonical_channel_name ch @config.canonical_channel_name ch end def clear_channels_member @current_channels.each{|ch, cs| cs.clear_members } end # def on_join user, rch ch = canonical_channel_name(rch) if user == nick chs = @current_channels[ch] = ChannelState.new(rch) else if @current_channels.has_key? ch @current_channels[ch].on_join(user) end end end def on_part user, rch ch = canonical_channel_name(rch) if user == nick @current_channels.delete ch else if @current_channels.has_key? ch @current_channels[ch].on_part user end end end def on_nick user, newnick, msg if user == nick @current_nick = newnick @try_nick = nil end # logging @current_channels.each{|ch, chs| if chs.on_nick user, newnick @config.logger.logging_nick ch, chs.name, user, newnick, msg end } end def on_quit user, qmsg, msg if user == nick @current_channels = {} # clear else # logging @current_channels.each{|ch, chs| if chs.on_part(user) @config.logger.logging_quit ch, chs.name, user, qmsg, msg @manager.invoke_event :invoke_bot, :quit_from_channel, chs.name, user, qmsg end } end end def on_mode user, rch, args ch = canonical_channel_name(rch) if @current_channels.has_key? ch @current_channels[ch].on_mode user, args end end def on_kick kicker, rch, user, comment ch = canonical_channel_name(rch) if user == nick @current_channels.delete ch else if @current_channels.has_key? ch @current_channels[ch].on_kick user end end end def on_topic user, rch, topic ch = canonical_channel_name(rch) if @current_channels.has_key? ch @current_channels[ch].topic = topic end end def on_332 rch, topic ch = canonical_channel_name(rch) if @current_channels.has_key? ch @current_channels[ch].topic = topic end end # RPL_NAMREPLY # ex) :lalune 353 test_ndk = #nadoka :test_ndk ko1_nmdk # def on_353 rch, users ch = canonical_channel_name(rch) if @current_channels.has_key? ch chs = @current_channels[ch] users.split(/ /).each{|e| /^([\@\+])?(.+)/ =~ e case $1 when '@' mode = 'o' when '+' mode = 'v' else mode = '' end chs.on_join $2, mode } # change initial mode if @config.channel_info[ch] && (im = @config.channel_info[ch][:initial_mode]) && chs.members.size == 1 @manager.send_to_server Cmd.mode(rch, im) end end end def safe_channel? ch ch[0] == ?! end # ERR_NOSUCHCHANNEL # ex) :NadokaProgram 403 simm !hoge :No such channel def on_403 ch if safe_channel?(ch) && ch[1] != ?! @manager.join_to_channel( "!" + ch) end end end end nadoka-0.7.6/ndk/version.rb0000644000000000000000000000166611450643270014260 0ustar rootroot# # Copyright (c) 2004-2005 SASADA Koichi # # 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: version.rb 239 2010-09-29 14:01:28Z znz $ # Create : K.S. 04/04/27 16:35:40 # module Nadoka NDK_Version = '0.7.6' NDK_Created = Time.now if /trunk/ =~ '$HeadURL: svn+ssh://rubyforge.org/var/svn/nadoka/trunk/ndk/version.rb $' NDK_Version.concat('-trunk') rev = '-' $LOAD_PATH.each{|path| path = path + '/ChangeLog' if FileTest.exist?(path) if /^\# ChangeLog of Nadoka\(\$Rev: (\d+) \$\)$/ =~ open(path){|f| s = f.gets} rev = "rev: #{$1}" break end end } NDK_Version.concat(" (#{rev})") end def self.version "Nadoka Ver.#{NDK_Version}" + " with Ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]" end end nadoka-0.7.6/ndk/config.rb0000644000000000000000000003416011243276636014043 0ustar rootroot# # Copyright (c) 2004-2005 SASADA Koichi # # 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: config.rb 204 2009-08-20 16:45:18Z znz $ # Create : K.S. 04/04/17 16:50:33 # # # You can check RCFILE with following command: # # ruby ndk_config.rb [RCFILE] # require 'uri' require 'socket' require 'kconv' require 'ndk/logger' module Nadoka class NDK_ConfigBase # system # 0: quiet, 1: normal, 2: system, 3: debug Loglevel = 2 Setting_name = nil # client server Client_server_port = 6667 # or nil (no listen) Client_server_host = nil Client_server_pass = 'NadokaPassWord' # or nil Client_server_acl = nil ACL_Object = nil # Server_list = [ # { :host => '127.0.0.1', :port => 6667, :pass => nil } ] Servers = [] Reconnect_delay = 150 Default_channels = [] Login_channels = [] # User = ENV['USER'] || ENV['USERNAME'] || 'nadokatest' Nick = 'ndkusr' Hostname = Socket.gethostname Servername = '*' Realname = 'nadoka user' Mode = nil Away_Message = 'away' Away_Nick = nil Quit_Message = "Quit Nadoka #{::Nadoka::NDK_Version} - http://www.atdot.net/nadoka/" # Channel_info = {} # log Default_log = { :file => '${setting_name}-${channel_name}/%y%m%d.log', :time_format => '%H:%M:%S', :message_format => { 'PRIVMSG' => '<{nick}> {msg}', 'NOTICE' => '{{nick}} {msg}', 'JOIN' => '+ {nick} ({prefix:user}@{prefix:host})', 'NICK' => '* {nick} -> {newnick}', 'QUIT' => '- {nick} (QUIT: {msg}) ({prefix:user}@{prefix:host})', 'PART' => '- {nick} (PART: {msg}) ({prefix:user}@{prefix:host})', 'KICK' => '- {nick} kicked by {kicker} ({msg})', 'MODE' => '* {nick} changed mode ({msg})', 'TOPIC' => '<{ch} TOPIC> {msg} (by {nick})', 'SYSTEM' => '[NDK] {orig}', 'OTHER' => '{orig}', 'SIMPLE' => '{orig}', }, } System_log = { :file => '${setting_name}-system.log', :time_format => '%y/%m/%d-%H:%M:%S', :message_format => { 'PRIVMSG' => '{ch} <{nick}> {msg}', 'NOTICE' => '{ch} {{nick}} {msg}', 'JOIN' => '{ch} + {nick} ({prefix:user}@{prefix:host})', 'NICK' => '{ch} * {nick} -> {newnick}', 'QUIT' => '{ch} - {nick} (QUIT: {msg}) ({prefix:user}@{prefix:host})', 'PART' => '{ch} - {nick} (PART: {msg}) ({prefix:user}@{prefix:host})', 'KICK' => '{ch} - {nick} kicked by {kicker} ({msg})', 'MODE' => '{ch} * {nick} changed mode ({msg})', 'TOPIC' => '{ch} <{ch} TOPIC> {msg} (by {nick})', 'SYSTEM' => '[NDK] {orig}', 'OTHER' => nil, 'SIMPLE' => nil, }, } Debug_log = { :io => $stdout, :time_format => '%y/%m/%d-%H:%M:%S', :message_format => System_log[:message_format], } Talk_log = { :file => '${setting_name}-talk/%y%m%d.log', :time_format => Default_log[:time_format], :message_format => { 'PRIVMSG' => '[{sender} => {receiver}] {msg}', 'NOTICE' => '{{sender} -> {receiver}} {msg}', } } System_Logwriter = nil Debug_Logwriter = nil Default_Logwriter = nil Talk_Logwriter = nil BackLog_Lines = 20 # file name encoding setting # 'euc' or 'sjis' or 'jis' or 'utf8' FilenameEncoding = case RUBY_PLATFORM when /mswin/, /cygwin/, /mingw/ 'sjis' else if /UTF-?8/i =~ ENV['LANG'] 'utf8' else 'euc' end end # dirs Plugins_dir = './plugins' Log_dir = './log' # bots BotConfig = [] # filters Privmsg_Filter = [] Notice_Filter = [] Primitive_Filters = {} # ... Privmsg_Filter_light = [] Nadoka_server_name = 'NadokaProgram' def self.inherited subklass ConfigClass << subklass end end ConfigClass = [NDK_ConfigBase] class NDK_Config NDK_ConfigBase.constants.each{|e| eval %Q{ def #{e.downcase} @config['#{e.downcase}'.intern] end } } def initialize manager, rcfile = nil @manager = manager @bots = [] load_config(rcfile || './nadokarc') end attr_reader :config, :bots, :logger def remove_previous_setting # remove setting class klass = ConfigClass.shift while k = ConfigClass.shift Object.module_eval{ remove_const(k.name) } end ConfigClass.push(klass) # clear required files RequiredFiles.replace [] # remove current NadokaBot Object.module_eval %q{ remove_const :NadokaBot module NadokaBot def self.included mod Nadoka::NDK_Config::BotClasses['::' + mod.name.downcase] = mod end end } # clear bot class BotClasses.each{|k, v| Object.module_eval{ if /\:\:/ !~ k.to_s && const_defined?(v.name) remove_const(v.name) end } } BotClasses.clear # destruct bot instances @bots.each{|bot| bot.bot_destruct } @bots = [] GC.start end def load_bots # for compatibility return load_bots_old if @config[:botconfig].kind_of? Hash @bots = @config[:botconfig].map{|bot| if bot.kind_of? Hash next nil if bot[:disable] name = bot[:name] cfg = bot raise "No bot name specified. Check rcfile." unless name else name = bot cfg = nil end load_botfile name.to_s.downcase make_bot_instance name, cfg }.compact end # for compatibility def load_bots_old (@config[:botfiles] + (@config[:defaultbotfiles]||[])).each{|file| load_botfile file } @config[:botconfig].keys.each{|bk| bkn = bk.to_s bkni= bkn.intern unless BotClasses.any?{|n, c| n == bkni} if @config[:botfiles] raise "No such BotClass: #{bkn}" else load_botfile "#{bkn.downcase}.nb" end end } @bots = BotClasses.map{|bkname, bk| if @config[:botconfig].has_key? bkname if (cfg = @config[:botconfig][bkname]).kind_of? Array cfg.map{|c| make_bot_instance bk, c } else make_bot_instance bk, cfg end else make_bot_instance bk, nil end }.flatten end def server_setting if svrs = @config[:servers] svl = [] svrs.each{|si| ports = si[:port] || 6667 host = si[:host] pass = si[:pass] ssl_params = si[:ssl_params] if ports.respond_to? :each ports.each{|port| svl << {:host => host, :port => port, :pass => pass, :ssl_params => ssl_params} } else svl << {:host => host, :port => ports, :pass => pass, :ssl_params => ssl_params} end } @config[:server_list] = svl end end def make_logwriter log return unless log case log when Hash if log.has_key?(:logwriter) return log[:logwriter] elsif log.has_key?(:logwriterclass) klass = log[:logwriterclass] elsif log.has_key?(:io) klass = IOLogWriter elsif log.has_key?(:file) klass = FileLogWriter else klass = FileLogWriter end opts = @config[:default_log].merge(log) klass.new(self, opts) when String opts = @config[:default_log].dup opts[:file] = log FileLogWriter.new(self, opts) when IO opts = @config[:default_log].dup opts[:io] = log IOLogWriter.new(self, opts) else raise "Unknown LogWriter setting" end end def make_default_logwriter if @config[:default_log].kind_of? Hash dl = @config[:default_log] else # defult_log must be Hash dl = @config[:default_log] @config[:default_log] = NDK_ConfigBase::Default_log.dup end @config[:default_logwriter] ||= make_logwriter(dl) @config[:system_logwriter] ||= make_logwriter(@config[:system_log]) @config[:debug_logwriter] ||= make_logwriter(@config[:debug_log]) @config[:talk_logwriter] ||= make_logwriter(@config[:talk_log]) end def channel_setting # treat with channel information if chs = @config[:channel_info] dchs = [] lchs = [] cchs = {} chs.each{|ch, setting| ch = identical_channel_name(ch) setting = {} unless setting.kind_of?(Hash) if !setting[:timing] || setting[:timing] == :startup dchs << ch elsif setting[:timing] == :login lchs << ch end # log writer setting[:logwriter] ||= make_logwriter(setting[:log]) || @config[:default_logwriter] cchs[ch] = setting } chs.replace cchs @config[:default_channels] = dchs @config[:login_channels] = lchs end end def acl_setting if @config[:client_server_acl] && !@config[:acl_object] require 'drb/acl' acl = @config[:client_server_acl].strip.split(/\s+/) @config[:acl_object] = ACL.new(acl) @logger.slog "ACL: #{acl.join(' ')}" end end def load_config(rcfile) load(rcfile) if rcfile @config = {} klass = ConfigClass.last klass.ancestors[0..-3].reverse_each{|kl| kl.constants.each{|e| @config[e.downcase.intern] = klass.const_get(e) } } @config[:setting_name] ||= File.basename(@manager.rc).sub(/\.?rc$/, '') if $NDK_Debug @config[:loglevel] = 3 end make_default_logwriter @logger = NDK_Logger.new(@manager, self) @logger.slog "load config: #{rcfile}" server_setting channel_setting acl_setting load_bots end def ch_config ch, key channel_info[ch] && channel_info[ch][key] end def canonical_channel_name ch ch = ch.sub(/^\!.{5}/, '!') identical_channel_name ch end def identical_channel_name ch # use 4 gsub() because of the compatibility of RFC2813(3.2) ch.toeuc.downcase.tr('[]\\~', '{}|^').tojis.force_encoding('ASCII-8BIT') end RName = { # ('&','#','+','!') '#' => 'CS-', '&' => 'CA-', '+' => 'CP-', '!' => 'CE-', } def make_logfilename tmpl, rch, cn unless cn cn = rch.sub(/^\!.{5}/, '!') case @config[:filenameencoding].to_s.downcase[0] when ?e # EUC cn = cn.toeuc.downcase when ?s # SJIS cn = cn.tosjis.downcase when ?u # utf-8 cn = cn.toutf8.downcase else # JIS cn = cn.toeuc.downcase.tojis cn = URI.encode(cn) end # escape cn = cn.sub(/^[\&\#\+\!]|/){|c| RName[c] } cn = cn.tr("*:/", "__I") end # format str = Time.now.strftime(tmpl) str.gsub(/\$\{setting_name\}/, setting_name). gsub(/\$\{channel_name\}|\{ch\}/, cn) end def log_format timefmt, msgfmts, msgobj text = log_format_message(msgfmts, msgobj) if timefmt && !msgobj[:nostamp] text = "#{msgobj[:time].strftime(timefmt)} #{text}" end text end def log_format_message msgfmts, msgobj type = msgobj[:type] format = msgfmts.fetch(type, @config[:default_log][:message_format][type]) if format.kind_of? Proc text = format.call(params) elsif format text = format.gsub(/\{([a-z]+)\}|\{prefix\:([a-z]+)\}/){|key| if $2 method = $2.intern if msgobj[:orig].respond_to?(:prefix) (msgobj[:orig].prefix || '') =~ /^(.+?)\!(.+?)@(.+)/ case method when :nick $1 when :user $2 when :host $3 else "!!unknown prefix attribute: #{method}!!" end end else if m = msgobj[$1.intern] m else "!!unknown attribute: #{$1}!!" end end } else text = msgobj[:orig].to_s end end def make_bot_instance bk, cfg bk = BotClasses[bk.to_s.downcase.intern] unless bk.kind_of? Class bot = bk.new @manager, self, cfg || {} @logger.slog "bot instance: #{bot.bot_state}" bot end def load_botfile file loaded = false if @config[:plugins_dir].respond_to? :each @config[:plugins_dir].each{|dir| if load_file File.expand_path("#{file}.nb", dir) loaded = true break end } else loaded = load_file File.expand_path("#{file}.nb", @config[:plugins_dir]) end unless loaded raise "No such bot file: #{file}" end end def load_file file if FileTest.exist? file Nadoka.require_bot file true else false end end RequiredFiles = [] BotClasses = {} end def self.require_bot file return if NDK_Config::RequiredFiles.include? file NDK_Config::RequiredFiles.push file begin ret = ::Kernel.load(file) rescue NDK_Config::RequiredFiles.pop raise end ret end end module NadokaBot # empty module for bot namespace # this module is reloadable def self.included mod Nadoka::NDK_Config::BotClasses['::' + mod.name.downcase] = mod end end if $0 == __FILE__ require 'pp' pp Nadoka::NDK_Config.new(nil, ARGV.shift) end nadoka-0.7.6/ndk/logger.rb0000644000000000000000000001565710770170155014060 0ustar rootroot# # Copyright (c) 2004-2005 SASADA Koichi # # 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: logger.rb 189 2008-03-19 10:52:29Z znz $ # Create : K.S. 04/05/01 02:04:18 require 'kconv' require 'fileutils' require 'thread' module Nadoka class LogWriter def initialize config, opts @opts = opts @config = config @time_fmt = opts[:time_format] @msg_fmts = opts[:message_format] end def write_log msg raise "override me" end def log_format msgobj @config.log_format @time_fmt, @msg_fmts, msgobj end def logging msgobj msg = log_format(msgobj) return if msg.empty? write_log msg end end class LogUnWriter < LogWriter def logging _ end end class IOLogWriter < LogWriter Lock = Mutex.new def initialize config, opts super @io = opts[:io] end def write_log msg Lock.synchronize{ @io.puts msg } end end class FileLogWriter < LogWriter def initialize config, opts super @filename_fmt = opts[:file] @channel_name_in_file_name = opts[:channel_name_in_file_name] end def logging msgobj msg = log_format(msgobj) return if msg.empty? write_log_file make_logfilename(@filename_fmt, msgobj[:ch] || '', @channel_name_in_file_name), msg end def write_log_file basefile, msg basedir = File.expand_path(@config.log_dir) + '/' logfile = File.expand_path(basefile, basedir) ldir = File.dirname(logfile) + '/' if !FileTest.directory?(ldir) raise "insecure directory: #{ldir} (pls check rc file.)" if /\A#{Regexp.quote(basedir)}/ !~ ldir # make directory recursively FileUtils.mkdir_p(ldir) end open(logfile, 'a'){|f| f.flock(File::LOCK_EX) f.puts msg } end def make_logfilename tmpl, ch, cn @config.make_logfilename tmpl, ch, cn end end class NDK_Logger class MessageStore def initialize limit @limit = limit @pool = [] end attr_reader :pool def limit=(lim) @limit = lim end def truncate while @pool.size > @limit @pool.shift end end def push msgobj truncate @pool.push msgobj end def clear @pool.clear end end class MessageStoreByTime < MessageStore def truncate lim = Time.now.to_i - @limit while true if @pool[0][:time].to_i < lim @pool.shift else break end end end end class MessageStores def initialize type, lim, config @limit = lim @class = type == :time ? MessageStoreByTime : MessageStore @config = config @pools = {} end attr_reader :pools def push msgobj ch = msgobj[:ccn] unless pool = @pools[ch] limit = (@config.channel_info[ch] && @config.channel_info[ch][:backloglines]) || @limit @pools[ch] = pool = @class.new(limit) end pool.push msgobj end def each_channel_pool @pools.each{|ch, store| yield ch, store.pool } end end def initialize manager, config @manager = manager @config = config @dlog = @config.debug_log @message_stores = MessageStores.new(:size, @config.backlog_lines, @config) end attr_reader :message_stores # debug message def dlog msg if @config.loglevel >= 3 msgobj = make_msgobj msg, 'DEBUG' @config.debug_logwriter.logging msgobj end end alias debug dlog # system message def slog msg, nostamp = false msgobj = make_msgobj(msg, 'SYSTEM', nostamp) if @config.loglevel >= 2 @config.system_logwriter.logging msgobj @message_stores.push msgobj end str = @config.system_logwriter.log_format(msgobj) @manager.send_to_clients Cmd.notice(@manager.state.nick, str) if @manager.state dlog str end # channel message def clog ch, msg, nostamp = false clog_msgobj ch, make_msgobj(msg, 'SIMPLE', nostamp, ch) end # other irc log message def olog msg olog_msgobj make_msgobj(msg, 'OTHER') end ######################################### def make_msgobj msg, type = msg.command, nostamp = false, ch = nil msgobj = { :time => Time.now, :type => type, :orig => msg, :nostamp => nostamp, :ch => ch, } msgobj end def clog_msgobj ch, msgobj if msgobj[:ccn] == :__talk__ logwriter = @config.talk_logwriter else logwriter = (@config.channel_info[ch] && @config.channel_info[ch][:logwriter]) || @config.default_logwriter end @message_stores.push msgobj logwriter.logging msgobj end def olog_msgobj msgobj if @config.loglevel >= 1 @config.system_logwriter.logging msgobj @message_stores.push msgobj end end # logging def logging msg user = @manager.nick_of(msg) rch = msg.params[0] ch_ = ch = @config.canonical_channel_name(rch) msgobj = make_msgobj(msg) msgobj[:ch] = rch # should be raw msgobj[:ccn] = ch msgobj[:nick] = user msgobj[:msg] = msg.params[1] case msg.command when 'PRIVMSG', 'NOTICE', 'TOPIC', 'JOIN', 'PART' unless /\A[\&\#\+\!]/ =~ ch # talk? msgobj[:sender] = user msgobj[:receiver] = rch msgobj[:ccn] = :__talk__ end clog_msgobj ch, msgobj when 'NICK', 'QUIT' # ignore. see below. when 'MODE' msgobj[:msg] = msg.params[1..-1].join(', ') if @manager.state.current_channels[ch] clog_msgobj ch, msgobj else olog_msgobj msgobj end when 'KICK' msgobj[:kicker] = msg.params[1] msgobj[:msg] = msg.params[2] clog_msgobj ch, msgobj when /^\d+/ # reply str = msg.command + ' ' + msg.params.join(' ') olog str else # other command olog msg.to_s end end def logging_nick ccn, rch, nick, newnick, msg msgobj = make_msgobj(msg) msgobj[:ch] = rch # should be raw msgobj[:ccn] = ccn msgobj[:nick] = nick msgobj[:newnick] = newnick clog_msgobj ccn, msgobj end def logging_quit ccn, rch, user, qmsg, msg msgobj = make_msgobj(msg) msgobj[:ch] = rch # should be raw msgobj[:ccn] = ccn msgobj[:nick] = user msgobj[:msg] = qmsg clog_msgobj ccn, msgobj end ### end end nadoka-0.7.6/ndk/error.rb0000644000000000000000000000201111256173224013706 0ustar rootroot# # Copyright (c) 2004-2005 SASADA Koichi # # 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: error.rb 218 2009-09-22 16:08:52Z znz $ # Create : K.S. 04/04/20 23:57:17 # module Nadoka class NDK_Error < Exception end class NDK_QuitClient < NDK_Error end class NDK_BotBreak < NDK_Error end class NDK_BotSendCancel < NDK_Error end class NDK_QuitProgram < NDK_Error end class NDK_RestartProgram < NDK_Error end class NDK_ReconnectToServer < NDK_Error end class NDK_InvalidMessage < NDK_Error end #### class NDK_FilterMessage_SendCancel < NDK_Error end class NDK_FilterMessage_Replace < NDK_Error def initialize msg @msg = msg end attr_reader :msg end class NDK_FilterMessage_OnlyBot < NDK_Error end class NDK_FilterMessage_OnlyLog < NDK_Error end class NDK_FilterMessage_BotAndLog < NDK_Error end end nadoka-0.7.6/ndk/server.rb0000644000000000000000000004741611457614155014113 0ustar rootroot# # Copyright (c) 2004-2005 SASADA Koichi # # 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: server.rb 243 2010-10-20 16:27:25Z znz $ # Create : K.S. 04/04/17 17:00:44 # require 'rice/irc' require 'ndk/error' require 'ndk/config' require 'ndk/server_state' require 'ndk/client' module Nadoka Cmd = ::RICE::Command Rpl = ::RICE::Reply class NDK_Server TimerIntervalSec = 60 MAX_PONG_FAIL = 5 def initialize rc @rc = rc @clients = [] @prev_timer = Time.now @server_thread = nil @clients_thread = nil @state = nil @state = NDK_State.new self reload_config @server = nil @cserver = nil @connected = false @exitting = false @pong_recieved = true @pong_fail_count = 0 @isupport = {} set_signal_trap end attr_reader :state, :connected, :rc attr_reader :isupport def client_count @clients.size end def next_server_info svinfo = @config.server_list.sort_by{rand}.shift @config.server_list.push svinfo [svinfo[:host], svinfo[:port], svinfo[:pass], svinfo[:ssl_params]] end def reload_config @config.remove_previous_setting if defined?(@config) @config = NDK_Config.new(self, @rc) # reset logger @logger = @config.logger @state.logger = @logger @state.config = @config @clients.each{|c| c.logger = @logger } end def start_server_thread @server_thread = Thread.new{ begin @server = make_server() @logger.slog "Server connection to #{@server.server}:#{@server.port}." @pong_recieved = true @server.start(1){|sv| sv << Cmd.quit(@config.quit_message) if @config.quit_message } rescue RICE::Connection::Closed, SystemCallError, IOError @connected = false part_from_all_channels @logger.slog "Connection closed by server. Trying to reconnect." sleep @config.reconnect_delay retry rescue NDK_ReconnectToServer @connected = false part_from_all_channels begin @server.close if @server rescue RICE::Connection::Closed, SystemCallError, IOError end @logger.slog "Reconnect request (no server response, or client request)." sleep @config.reconnect_delay retry rescue Exception => e ndk_error e @clients_thread.kill if @clients_thread && @clients_thread.alive? end } end def make_server host, port, @server_passwd, ssl_params = next_server_info server = ::RICE::Connection.new(host, port, "\r\n", ssl_params) server.regist{|rq, wq| Thread.stop @rq = rq begin @connected = false server_main_proc rescue Exception => e ndk_error e @server_thread.kill if @server_thread && @server_thread.alive? @clients_thread.kill if @clients_thread && @clients_thread.alive? ensure @server.close end } server end def server_main_proc ## login # send passwd if @server_passwd send_to_server Cmd.pass(@server_passwd) end # send nick if @config.away_nick && client_count == 0 @state.original_nick = @config.nick @state.nick = @config.away_nick else @state.nick = @config.nick end send_to_server Cmd.nick(@state.nick) # send user info send_to_server Cmd.user(@config.user, @config.hostname, @config.servername, @config.realname) # wait welcome message while q = recv_from_server case q.command when '001' break when '433' # Nickname is already in use. nick = @state.nick_succ(q.params[1]) @state.nick = nick send_to_server Cmd.nick(nick) when 'NOTICE' # ignore when 'ERROR' msg = "Server login fail!(#{q})" @server_thread.raise NDK_ReconnectToServer else msg = "Server login fail!(#{q})" @logger.slog msg raise msg end end # change user mode if @config.mode send_to_server Cmd.mode(@state.nick, @config.mode) end # join to default channels if @state.current_channels.size > 0 # if reconnect @state.current_channels.each{|ch, chs| join_to_channel ch } else # default join process @config.default_channels.each{|ch| join_to_channel ch } end @connected = true @isupport = {} ## if @clients.size == 0 enter_away end invoke_event :invoke_bot, :server_connected # loop while q = recv_from_server case q.command when 'PING' send_to_server Cmd.pong(q.params[0]) next when 'PRIVMSG' if ctcp_message?(q.params[1]) ctcp_message(q) end when 'JOIN' @state.on_join(nick_of(q), q.params[0]) when 'PART' @state.on_part(nick_of(q), q.params[0]) when 'NICK' @state.on_nick(nick_of(q), q.params[0], q) when 'QUIT' @state.on_quit(nick_of(q), q.params[0], q) when 'TOPIC' @state.on_topic(nick_of(q), q.params[0], q.params[1]) when 'MODE' @state.on_mode(nick_of(q), q.params[0], q.params[1..-1]) when 'KICK' @state.on_kick(nick_of(q), q.params[0], q.params[1], q.params[2]) when '353' # RPL_NAMREPLY @state.on_353(q.params[2], q.params[3]) when '332' # RPL_TOPIC @state.on_332(q.params[1], q.params[2]) when '403' # ERR_NOSUCHCHANNEL @state.on_403(q.params[1]) when '433', '436', '437' # ERR_NICKNAMEINUSE, ERR_NICKCOLLISION, ERR_UNAVAILRESOURCE # change try nick case q.params[1] when /\A[\#&!+]/ # retry join after 1 minute Thread.start(q.params[1]) do |ch| sleep 60 join_to_channel ch end else nick = @state.nick_succ(q.params[1]) send_to_server Cmd.nick(nick) @logger.slog("Retry nick setting: #{nick}") end when '005' # RPL_ISUPPORT or RPL_BOUNCE if /supported/i =~ q.params[-1] q.params[1..-2].each do |param| if /\A(-)?([A-Z0-9]+)(?:=(.*))?\z/ =~ param negate, key, value = $~.captures if negate @isupport.delete(key) else @isupport[key] = value || true end end end end @logger.dlog "isupport: #{@isupport.inspect}" else # end send_to_clients q @logger.logging q send_to_bot q end end def join_to_channel ch if @config.channel_info[ch] && @config.channel_info[ch][:key] send_to_server Cmd.join(ch, @config.channel_info[ch][:key]) else send_to_server Cmd.join(ch) end end def enter_away return if @exitting || !@connected send_to_server Cmd.away(@config.away_message) if @config.away_message # change nick if @state.nick != @config.away_nick && @config.away_nick @state.original_nick = @state.nick send_to_server Cmd.nick(@config.away_nick) end # part channel @config.login_channels.each{|ch| if @config.channel_info[ch] && @state.channels.include?(ch) if @config.channel_info[ch][:part_message] send_to_server Cmd.part(ch, @config.channel_info[ch][:part_message]) else send_to_server Cmd.part(ch) end end } end def leave_away return if @exitting || !@connected send_to_server Cmd.away() if @config.away_nick && @state.original_nick sleep 2 # wait for server response send_to_server Cmd.nick(@state.original_nick) @state.original_nick = nil sleep 1 # wait for server response end @config.login_channels.each{|ch| send_to_server Cmd.join(ch) } end def start_clients_thread return unless @config.client_server_port @clients_thread = Thread.new{ begin @cserver = TCPServer.new(@config.client_server_host, @config.client_server_port) @logger.slog "Open Client Server Port: #{@cserver.addr.join(' ')}" while true # wait for client connections Thread.start(@cserver.accept){|cc| client = nil begin if !@config.acl_object || @config.acl_object.allow_socket?(cc) client = NDK_Client.new(@config, cc, self) @clients << client client.start else @logger.slog "ACL denied: #{cc.peeraddr.join(' ')}" end rescue Exception => e ndk_error e ensure @clients.delete client invoke_event :enter_away, client_count cc.close unless cc.closed? end } end rescue Exception => e ndk_error e ensure @clients.each{|cl| cl.kill } if @cserver @logger.slog "Close Client Server Port: #{@cserver.addr.join(' ')}" @cserver.close unless @cserver.closed? end @server_thread.kill if @server_thread.alive? end } end def start start_server_thread start_clients_thread timer_thread = Thread.new{ begin @pong_recieved = true @pong_fail_count = 0 while true slp = Time.now.to_i % TimerIntervalSec slp = TimerIntervalSec if slp < (TimerIntervalSec / 2) sleep slp send_to_bot :timer, Time.now if @connected if @pong_recieved @pong_fail_count = 0 else # fail @pong_fail_count += 1 @logger.slog "PONG MISS: #{@pong_fail_count}" if @pong_fail_count > MAX_PONG_FAIL @pong_fail_count = 0 invoke_event :reconnect_to_server end end @pong_recieved = false @server << Cmd.ping(@server.server) else @pong_recieved = true @pong_fail_count = 0 end end rescue Exception => e ndk_error e end } begin @server_thread.join rescue Interrupt @exitting = true ensure @server_thread.kill if @server_thread && @server_thread.alive? @clients_thread.kill if @clients_thread && @clients_thread.alive? timer_thread.kill if timer_thread && timer_thread.alive? @server.close if @server end end def send_to_server msg str = msg.to_s if /[\r\n]/ =~ str.chomp @logger.dlog "![>S] #{str}" raise NDK_InvalidMessage, "Message must not include [\\r\\n]: #{str.inspect}" else @logger.dlog "[>S] #{str}" @server << msg end end def recv_from_server while q = @rq.pop # Event if q.kind_of? Array exec_event q next end # Server -> Nadoka message if !@config.primitive_filters.nil? && !@config.primitive_filters[q.command].nil? && !@config.primitive_filters[q.command].empty? next unless filter_message(@config.primitive_filters[q.command], q) end case q.command when 'PING' @server << Cmd.pong(q.params[0]) when 'PONG' @pong_recieved = true when 'NOTICE' @logger.dlog "[ e @logger.dlog "[NDK] Message Replaced: #{e}" return e.msg rescue NDK_FilterMessage_OnlyBot @logger.dlog "[NDK] Message only bot" send_to_bot msg return false rescue NDK_FilterMessage_OnlyLog @logger.dlog "[NDK] Message only log" @logger.logging msg return false rescue NDK_FilterMessage_BotAndLog @logger.dlog "[NDK] Message log and bot" send_to_bot msg @logger.logging msg return false end msg end def invoke_event ev, *arg arg.unshift ev @rq && (@rq << arg) end def exec_event q # special event case q[0] when :reload_config # q[1] must be client object begin reload_config @logger.slog "configuration is reloaded" rescue Exception => e @logger.slog "error is occure while reloading configuration" ndk_error e end when :quit_program @exitting = true Thread.main.raise NDK_QuitProgram when :restart_program @exitting = true Thread.main.raise NDK_RestartProgram when :reconnect_to_server @connected = false @server_thread.raise NDK_ReconnectToServer when :invoke_bot # q[1], q[2] are message and argument send_to_bot q[1], *q[2..-1] when :enter_away if q[1] == 0 enter_away end when :leave_away if q[1] == 1 leave_away end end end def set_signal_trap list = Signal.list Signal.trap(:INT){ # invoke_event :quit_program Thread.main.raise NDK_QuitProgram } if list['INT'] Signal.trap(:TERM){ # invoke_event :quit_program Thread.main.raise NDK_QuitProgram } if list.any?{|e| e == 'TERM'} Signal.trap(:HUP){ # reload config invoke_event :reload_config } if list['HUP'] trap(:USR1){ # SIGUSR1 invoke_event :invoke_bot, :sigusr1 } if list['USR1'] trap(:USR2){ # SIGUSR2 invoke_event :invoke_bot, :sigusr2 } if list['USR2'] end def about_me? msg qnick = Regexp.quote(@state.nick || '') if msg.prefix =~ /^#{qnick}!/ true else false end end def own_nick_change? msg if msg.command == 'NICK' && msg.params[0] == @state.nick nick_of(msg) else false end end def part_from_all_channels @state.channels.each{|ch, cs| cmd = Cmd.part(ch) cmd.prefix = @state.nick #m send_to_clients cmd } @state.clear_channels_member end # server -> clients def send_to_clients msg if msg.command == 'PRIVMSG' && !(msg = filter_message(@config.privmsg_filter_light, msg)) return end if(old_nick = own_nick_change?(msg)) @clients.each{|cl| cl.add_prefix2(msg, old_nick) cl << msg } elsif about_me? msg @clients.each{|cl| cl.add_prefix(msg) cl << msg } else @clients.each{|cl| cl << msg } end end def ping_to_clients @clients.each{|cl| cl << Cmd.ping(cl.remote_host) } end # clientA -> other clients # bot -> clients def send_to_clients_otherwise msg, elt @clients.each{|cl| if cl != elt cl.add_prefix(msg) unless msg.prefix cl << msg end } invoke_event :invoke_bot, msg if elt @logger.logging msg end def ctcp_message? arg arg[0] == ?\x1 end def ctcp_message msg if /\001(.+)\001/ =~ msg.params[1] ctcp_cmd = $1 case ctcp_cmd when 'VERSION' send_to_server Cmd.notice(nick_of(msg), "\001VERSION #{Nadoka.version}\001") when 'TIME' send_to_server Cmd.notice(nick_of(msg), "\001TIME #{Time.now}\001") else end end end def nick_of msg if /^([^!]+)\!?/ =~ msg.prefix.to_s $1 else @state.nick end end class PrefixObject def initialize prefix parse_prefix prefix @prefix = prefix end attr_reader :nick, :user, :host, :prefix def parse_prefix prefix if /^(.+?)\!(.+?)@(.+)/ =~ prefix.to_s # command @nick, @user, @host = $1, $2, $3 else # server reply @nick, @user, @host = nil, nil, prefix end end def to_s @prefix end end def make_prefix_object msg prefix = msg.prefix if prefix PrefixObject.new(prefix) else if /^d+$/ =~ msg.command PrefixObject.new(@config.nadoka_server_name) else PrefixObject.new("#{@state.nick}!#{@config.user}@#{@config.nadoka_server_name}") end end end # dispatch to bots def send_to_bot msg, *arg selector = 'on_' + if msg.respond_to? :command if /^\d+$/ =~ msg.command # reply prefix = make_prefix_object msg RICE::Reply::Replies_num_to_name[msg.command] else # command prefix = make_prefix_object msg msg.command.downcase end else prefix = nil msg.to_s end @config.bots.each{|bot| begin if bot.respond_to? selector unless prefix bot.__send__(selector, *arg) else bot.__send__(selector, prefix, *msg.params) end end if prefix && bot.respond_to?(:on_every_message) bot.__send__(:on_every_message, prefix, msg.command, *msg.params) end rescue NDK_BotBreak break rescue NDK_BotSendCancel return false rescue Exception ndk_error $! end } true end def ndk_status [ '== Nadoka Running Status ==', '- nadoka version: ' + Nadoka.version, '- connecting to ' + "#{@server.server}:#{@server.port}", '- clients status:', @clients.map{|e| '-- ' + e.state}, '- Bots status:', @config.bots.map{|bot| '-- ' + bot.bot_state}, '== End of Status ==' ].flatten end def ndk_error err @logger.slog "Exception #{err.class} - #{err}" @logger.slog "-- backtrace --" err.backtrace.each{|line| @logger.slog "| " + line } end end end nadoka-0.7.6/lib/0000755000000000000000000000000011640634705012233 5ustar rootrootnadoka-0.7.6/lib/rss_check.rb0000644000000000000000000001020211233744426014517 0ustar rootroot# # Copyright (c) 2004-2005 SASADA Koichi # # 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: rss_check.rb 201 2009-07-29 03:59:18Z znz $ # Create : K.S. Sat, 24 Apr 2004 12:10:31 +0900 # require "rss/parser" require "rss/1.0" require "rss/2.0" require "rss/syndication" require "rss/dublincore" require "open-uri" require 'uri' require 'yaml/store' require 'csv' require 'stringio' require 'zlib' class RSS_Check class RSS_File def initialize path, init_now @uri = URI.parse(path) @entry_time = @file_time = (init_now ? Time.now : Time.at(0)) end def check begin if (mt=mtime) > @file_time @file_time = mt check_entries else [] end rescue => e [{ :about => e.message, :title => "RSS Check Error (#{@uri})", :ccode => 'UTF-8' }] end end def date_of e if e.respond_to? :dc_date e.dc_date || Time.at(0) else e.pubDate || Time.at(0) end end def check_entries rss = RSS::Parser.parse(read_content, false) et = @entry_time items = rss.items.sort_by{|e| date_of(e) }.map{|e| e_date = date_of(e) if e_date > @entry_time if e_date > et et = e_date end { :about => e.about, :title => e.title, :ccode => 'UTF-8' } end }.compact @entry_time = et items end def read_content case @uri.scheme when 'http' open(@uri){|f| if f.content_encoding.any?{|e| /gzip/ =~ e} Zlib::GzipReader.new(StringIO.new(f.read)).read || '' else f.read end } else open(@uri.to_s){|f| f.read } end end def mtime case @uri.scheme when 'http' open(@uri){|f| f.last_modified || Time.now } else File.mtime(@rss_file) end end end class LIRS_File < RSS_File def check_entries et = @entry_time res = [] CSV::Reader.parse(read_content){|row| last_detected = Time.at(row[2].data.to_i) if last_detected > @entry_time && row[1].data != row[2].data if last_detected > et et = last_detected end res << { :about => row[5].data, :title => row[6].data, :ccode => 'EUC-JP' } end } @entry_time = et res end end def initialize paths, cache_file=nil, init_now=false @paths = paths @db = YAML::Store.new(cache_file) if cache_file @rss_files = paths.map{|uri| load_file(uri) || if /LIRS:(.+)/ =~ uri LIRS_File.new($1, init_now) else RSS_File.new(uri, init_now) end } end def check @rss_files.map{|rf| rf.check }.flatten end def save @db.transaction{ @paths.each_with_index{|path, i| @db[path] = @rss_files[i] } } if @db end def load_file file @db.transaction{ @db[file] } if @db end def clear if @db @db.transaction{ @db.keys.each{|k| @db.delete k } } end end end if $0 == __FILE__ rss_uri = %w( http://www.ruby-lang.org/ja/index.rdf http://slashdot.jp/slashdotjp.rss http://www3.asahi.com/rss/index.rdf http://pcweb.mycom.co.jp/haishin/rss/index.rdf http://japan.cnet.com/rss/index.rdf http://blog.japan.cnet.com/umeda/index.rdf http://jvn.doi.ics.keio.ac.jp/rss/jvnRSS.rdf ) lirs_uri = [ 'LIRS:http://www.rubyist.net/~kazu/samidare/sites.lirs.gz' ] rssc = RSS_Check.new( rss_uri + lirs_uri, ARGV.shift || './rss_cache', false # false ) require 'iconv' require 'kconv' ic = Iconv.open("EUC-JP", "UTF-8") rssc.check.each{|e| puts e[:about] title = (e[:ccode] == 'UTF-8') ? ic.iconv(e[:title]) : e[:title] puts title } rssc.dump end nadoka-0.7.6/lib/tagparts.rb0000644000000000000000000000742610566613051014413 0ustar rootroot# # Copyright (c) 2004-2005 SASADA Koichi # # 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:$ # require 'cgi' module TagParts class TagItem include Enumerable def initialize tag, body, ignore_empty = false @tag = tag.to_s @attr = {} @body = [] @ignore_empty = ignore_empty body.each{|e| add! e } end attr_reader :body, :tag, :attr def make_attr_str @attr.map{|k,v| " #{CGI.escapeHTML(k.to_s)}='#{CGI.escapeHTML(v)}'" }.join end def to_s if @body.size > 0 || @ignore_empty body = @body.flatten.map{|e| if e.kind_of? String CGI.escapeHTML(e.to_s) else e.to_s end } "<#{@tag}#{make_attr_str}\n>#{body}\n" else "<#{@tag}#{make_attr_str} /\n>" end end def inspect ">" end def add!(elem) if elem.kind_of? Hash @attr.update elem else @body << elem end end def [](k) @attr[k] end def []=(k, v) @attr[k] = v end def each @body.flatten.each{|e| yield e } end def each_leaf @body.each{|e| if e.kind_of? TagItem e.each_leaf(&Proc.new) else yield e end } end def each_node yield(self) @body.each{|e| if e.kind_of? TagItem e.each_node(&Proc.new) else yield e end } end alias << add! end def ignore_empty_tag? false end # do nothing. please override def tag_encoding str str end def tree2string tag tag_encoding(tree2string_(tag)) end def tree2string_ tag bs = tag.map{|body| if body.kind_of? TagItem tree2string_(body) else CGI.escapeHTML(body.to_s) end } tagname = tag.tag attr = tag.make_attr_str if bs.size > 0 || ignore_empty_tag? "<#{tagname}#{attr}\n>#{bs}\n" else "<#{tagname}#{attr}\n/>" end end @@method_prefix = '_' def self.newtag sym, ignore_empty, klass = TagParts klass.module_eval <<-EOS def #{@@method_prefix}#{sym}(*args) TagItem.new(:#{sym}, args, #{ignore_empty}) end EOS end TagParts.module_eval <<-EOS def #{@@method_prefix}(tag, *args) TagItem.new(tag, args, false) end EOS def method_missing m, *args if make_unknown_tag? && (/^#{@@method_prefix}(.+)/ =~ m.to_s) TagItem.new($1, args) else super end end def make_unknown_tag? true end end module HTMLParts include TagParts def make_unknown_tag? false end def ignore_empty_tag? true end # copy from cgi.rb PARTS_1 = %w{ TT I B BIG SMALL EM STRONG DFN CODE SAMP KBD VAR CITE ABBR ACRONYM SUB SUP SPAN BDO ADDRESS DIV MAP OBJECT H1 H2 H3 H4 H5 H6 PRE Q INS DEL DL OL UL LABEL SELECT OPTGROUP FIELDSET LEGEND BUTTON TABLE TITLE STYLE SCRIPT NOSCRIPT TEXTAREA FORM A BLOCKQUOTE CAPTION } PARTS_2 = %w{ IMG BASE BR AREA LINK PARAM HR INPUT COL META } PARTS_3 = %w{ HTML BODY P DT DD LI OPTION THEAD TFOOT TBODY COLGROUP TR TH TD HEAD } (PARTS_1 + PARTS_2 + PARTS_3).each{|e| elem = e.downcase TagParts.newtag elem, true } end __END__ include HTMLParts tags = _html( _head( _title('hogehoge')), _body( _br(), _h1('huga-'), _p('hogehoge', _a('hogehoge', 'href' => 'dokka'), 'huga'), _p('hogehoge', 'huga', ['ho-', 'hu']) )) puts tags.to_s puts tree2string(tags) p( tags.to_s == tree2string(tags) ) nadoka-0.7.6/nadokarc0000644000000000000000000001632311450645335013177 0ustar rootroot## -*-ruby-*- vim: set filetype=ruby : ## ## Nadoka Sample resource file ## $Id: nadokarc 240 2010-09-29 14:19:09Z znz $ # # Copyright (c) 2004-2005 SASADA Koichi # # 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 lisence. # # class NADOKA_Config < Nadoka::NDK_ConfigBase ############################################### # System setting # Setting_name = 'IRCNet' # # If you don't specify Setting_name, # # system use rc file name as setting name # # if your rc file is "test.rc", setting name is "test" # # (remove last .rc) # # 0: quiet # 1: ch log only # 2: normal # 3: debug Loglevel = 2 ############################################### # client server setting Client_server_port = 6667 # or nil (no listen) Client_server_host = nil # You can specify binding host(interface) Client_server_pass = 'NadokaPassWord' Client_server_acl = nil # ACL(Access Control List) example: # Client_server_acl = %q{ # deny all # allow 192.168.1.1 # allow 192.168.2.0/24 # } # ACL setting must be String # # or you can set acl directly like follows: # # ACL_Object = ::ACL.new(...) # ############################################### # server setting # IRCnet servers in Japan Servers = [ { :host => 'irc.ircnet.ne.jp', :port => 6667, # default: 6667 :pass => nil, # default: nil }, { :host => 'irc.media.kyoto-u.ac.jp', :port => (6660 .. 6669), }, { :host => 'irc.huie.hokudai.ac.jp', # without :port, use 6667 as default port }, # IPv6 Sample # { # :host => 'irc6.ircnet.ne.jp', # :port => 6667, # }, # { # :host => 'irc6.livedoor.ne.jp', # :port => (6660 .. 6669), # }, ] #Servers = [ # SSL Sample # { # :host => 'ircs.example.net', # :port => 6697, # :ssl_params => { # with :ssl_params hash, use ssl # # :ca_cert => "/etc/ssl/certs", # default: openssl's default certificates # }, # }, #] ############################################### # userinfo User = ENV['USER'] || ENV['USERNAME'] || 'nadokatest' Nick = ENV['USER'] || ENV['USERNAME'] || 'ndk_nick' Hostname = Socket.gethostname Realname = 'nadoka user' Mode = nil Away_Message = 'away' # If this item is String, your nick will # be that when no clients are connected. Away_Nick = nil # Quit_Message = "Quit Nadoka" ############################################### # channel info # log filename format # ${setting_name} : Setting name # ${channel_name} : Channel name # %? : Time#strftime format(see ruby reference) # Default_log = { :file => '${setting_name}-${channel_name}/%y%m%d.log', :time_format => '%H:%M:%S', :message_format => { 'PRIVMSG' => '<{nick}> {msg}', 'NOTICE' => '{{nick}} {msg}', 'JOIN' => '+ {nick} ({prefix:user}@{prefix:host})', 'NICK' => '* {nick} -> {newnick}', 'QUIT' => '- {nick} (QUIT: {msg}) ({prefix:user}@{prefix:host})', 'PART' => '- {nick} (PART: {msg}) ({prefix:user}@{prefix:host})', 'KICK' => '- {nick} was kicked by {kicker} ({msg})', 'MODE' => '* {nick} changed mode ({msg})', 'TOPIC' => '* TOPIC: {msg} (by {nick})', 'SYSTEM' => '[NDK] {orig}', 'OTHER' => '{orig}', 'SIMPLE' => '{orig}', }, } System_log = { :file => '${setting_name}-system.log', :time_format => '%y/%m/%d-%H:%M:%S', :message_format => { 'PRIVMSG' => '{ch} <{nick}> {msg}', 'NOTICE' => '{ch} {{nick}} {msg}', 'JOIN' => '{ch} + {nick} ({prefix:user}@{prefix:host})', 'NICK' => '{ch} * {nick} -> {newnick}', 'QUIT' => '{ch} - {nick} (QUIT: {msg}) ({prefix:user}@{prefix:host})', 'PART' => '{ch} - {nick} (PART: {msg}) ({prefix:user}@{prefix:host})', 'KICK' => '{ch} - {nick} was kicked by {kicker} ({msg})', 'MODE' => '{ch} * {nick} changed mode ({msg})', 'TOPIC' => '{ch} * TOPIC: {msg} (by {nick})', 'SYSTEM' => '[NDK] {orig}', 'OTHER' => nil, 'SIMPLE' => nil, }, } Talk_log = { :file => '${setting_name}-talk/%y%m%d.log', :time_format => Default_log[:time_format], :message_format => { 'PRIVMSG' => '[{sender} => {receiver}] {msg}', 'NOTICE' => '{{sender} => {receiver}} {msg}', } } Channel_info = { # nadoka talk (from Japanese server only) '#nadoka' => { # timing # :startup / when nadoka start up <= default # :login / when user login # otherwise nadoka won't login automatically :timing => :startup, :log => '${setting_name}-nadoka-chan/%y%m%d.log', # :part_message => "bye bye" # :initial_mode => "+s" }, '#nadoka:*.jp' => { :timing => :startup, :log => '${setting_name}-nadoka-chan-jp/%y%m%d.log', }, # nadoka bot channel '#nadoka_check' => { :timing => :startup, :log => '${setting_name}-nadoka-check/%y%m%d.log', # you can specify store backlog lines :backlog_lines => 5, }, # # '#log_setting_example' => { # :log => { # :file => 'logfilename', # log filename # # :io => $stderr, # or IO object is supported # :time_format => '%H:%M:%S', # # :channel_name_in_file_name => 'hoge' # # :message_format => { # 'PRIVMSG' => '<{nick}> {msg}', # 'NOTICE' => '{{nick}} {msg}', # 'JOIN' => '+ {nick} to {ch}', # 'NICK' => '* {nick} -> {newnick}', # 'QUIT' => '- {nick} ({msg})', # 'PART' => '- {nick} from {ch} ({msg})', # 'KICK' => '- {nick} kicked by {kicker} ({msg}) from {ch}', # 'MODE' => '* {nick} changed mode ({msg})', # 'TOPIC' => '<{ch} TOPIC> {msg}', # 'SYSTEM' => '[NDK] {msg}', # 'OTHER' => nil, # 'SIMPLE' => nil, # # # for more complex log output: # # :logwriterclass => your_log_writer_class, # # :logwriter => your_log_writer_instance, # # :other_logwriterclass_specific_setting => ..., # }, # }, } BackLog_Lines = 20 # # FilenameEncoding = 'euc' # FilenameEncoding = 'sjis' # FilenameEncoding = 'utf8' # # If you not specify FilenameEncoding, nadoka infers # suitable encoding (see ndk/config.rb). # ############################################### # Directory Log_dir = './log' Plugins_dir = './plugins' # You can set Plugins_dir as Enumerable object. # ex: ['./dir1', './dir2', ...] ############################################### # Bots BotConfig = [ # :BotName1, # :BotName1, # 'BotName1', # :BotName2, # { :name => :BotName3, # :set1 => setting1, # }, # { :name => :BotName3, # :set1 => setting2, # }, # { :name => :BotName4, # :set1 => setting3, # }, :BackLogBot, # strongly recommended ] ############################################### # Misc Privmsg_Filter = nil Notice_Filter = nil end nadoka-0.7.6/rice/0000755000000000000000000000000011640634705012407 5ustar rootrootnadoka-0.7.6/rice/irc.rb0000644000000000000000000004577311571541145013527 0ustar rootroot=begin = rice - Ruby Irc interfaCE Original Credit: Original Id: irc.rb,v 1.9 2001/06/13 10:22:24 akira Exp Copyright (c) 2001 akira yamada You can redistribute it and/or modify it under the same term as Ruby. == Modified Modified by K.Sasada. $Id: irc.rb 250 2011-06-01 22:51:17Z znz $ =end require 'socket' require 'thread' require 'monitor' begin require "openssl" rescue LoadError end module RICE class Error < StandardError; end class InvalidMessage < Error; end class UnknownCommand < Error; end =begin == RICE::Connection =end class Connection class Error < StandardError; end class Closed < Error; end =begin --- RICE::Connection::new =end def initialize(server, port, eol = "\r\n", ssl_params = nil) @conn = [] @conn.extend(MonitorMixin) @main_th = nil self.server = server self.port = port self.eol = eol self.ssl_params = ssl_params @read_q = Queue.new @read_th = Thread.new(@read_q, @eol) do |read_q, eol| read_thread(read_q, eol) end @threads = {} @threads.extend(MonitorMixin) @dispatcher = Thread.new(@read_q) do |read_q| loop do x = read_q.pop ths = @threads.synchronize do @threads.keys end ths.each do |th| if th.status @threads[th].q.push(x) else @threads.delete(th) end end end # loop end @delay = 0.3 @prev_send_time = Time.now end attr :delay, true attr_reader :server, :port, :ssl_params =begin --- RICE::Connection#server=(server) =end def server=(server) raise RuntimeError, "Already connected to #{@server}:#{@port}" unless @conn.empty? @server = server end =begin --- RICE::Connection#port=(port) =end def port=(port) raise RuntimeError, "Already connected to #{@server}:#{@port}" unless @conn.empty? @port = port end =begin --- RICE::Connection#eol=(eol) =end def eol=(eol) raise RuntimeError, "Already connected to #{@server}:#{@port}" unless @conn.empty? @eol = eol end =begin --- RICE::Connection#ssl_params=(ssl_params) =end def ssl_params=(ssl_params) raise RuntimeError, "Already connected to #{@server}:#{@port}" unless @conn.empty? unless ssl_params @ssl_params = false return end raise 'openssl library not installed' unless defined?(OpenSSL) ssl_params = ssl_params.to_hash ssl_params[:verify_mode] ||= OpenSSL::SSL::VERIFY_PEER store = OpenSSL::X509::Store.new if ssl_params.key?(:ca_cert) ca_cert = ssl_params.delete(:ca_cert) if ca_cert # auto setting ca_path or ca_file like open-uri.rb if File.directory? ca_cert store.add_path ca_cert else store.add_file ca_cert end else # use ssl_params={:ca_cert=>nil} if you want to disable auto setting store = nil end else # use default of openssl store.set_default_paths end if store ssl_params[:cert_store] = store end @ssl_params = ssl_params end =begin --- RICE::Connection#start(max_retry = 3, retry_wait = 30) =end def start(max_retry = 3, retry_wait = 30) @client_th = Thread.current # caller thread if alive? #sleep retry_wait return nil end @main_th = Thread.new do begin Thread.stop ensure yield(self) if block_given? @read_th.raise(Closed) if @read_th.status close(true) @client_th.raise(Closed) end end begin open_conn rescue SystemCallError max_retry -= 1 if max_retry == 0 @main_th.kill raise end sleep retry_wait retry ensure @main_th.join nil end end def open_conn @conn.synchronize do conn = TCPSocket.new(@server, @port) if ssl_params context = OpenSSL::SSL::SSLContext.new context.set_params(ssl_params) conn = OpenSSL::SSL::SSLSocket.new(conn, context) conn.sync_close = true conn.connect if context.verify_mode != OpenSSL::SSL::VERIFY_NONE conn.post_connection_check(@server) end end @conn[0] = conn end @conn[0].extend(MonitorMixin) @read_th.run ths = @threads.synchronize do @threads.keys end ths.each do |th| th.run if th.status && th.stop? end end private :open_conn =begin --- RICE::Connection#regist(raise_on_close, *args) {...} =end USER_THREAD = Struct.new('User_Thread', :q, :raise_on_close) def regist(raise_on_close = false, *args) read_q = Queue.new th = Thread.new(read_q, self, *args) do |read_q, conn, *args| yield(read_q, conn, *args) end @threads.synchronize do @threads[th] = USER_THREAD.new(read_q, raise_on_close) end th end =begin --- RICE::Connection#unregist(thread) =end def unregist(thread) th = nil @threads.synchronize do th = @threads.delete(th) end th.exit th end def read_thread(read_q, eol) begin read_q.clear Thread.stop begin conn = @conn[0] while l = conn.gets(eol) begin read_q.push(Message.parse(l)) rescue UnknownCommand $stderr.print l.inspect if $DEBUG rescue InvalidMessage begin read_q.push(Message.parse(l.sub(/\s*#{eol}\z/o, eol))) rescue $stderr.print l.inspect if $DEBUG end end end rescue IOError#, SystemCallError $stderr.print "#{self.inspect}: read_th get error #{$!}" if $DEBUG ensure raise Closed end rescue Closed begin @main_th.run if @main_th.alive? rescue Closed end retry end end private :read_thread =begin --- RICE::Connection#close(restart = false) =end def close(restart = false) begin unless restart @main_th.exit if @main_th.alive? @read_th.exit if @read_th.alive? end conn = nil @conn.synchronize do conn = @conn.shift end conn.close if conn @threads.synchronize do @threads.each_key do |th| if restart if @threads[th].raise_on_close if @threads[th].raise_on_close.kind_of?(Exception) th.raise(@threads[th].raise_on_close) else th.raise(Closed) end end else th.exit end end end end end =begin --- RICE::Connection#alive? =end def alive? @main_th && @main_th.alive? end =begin --- RICE::Connection#push(message) =end def push(message) conn = @conn[0] if conn conn.synchronize do cmd = message.command if cmd == 'PRIVMSG' || cmd == 'NOTICE' # flood control t = Time.now if t.to_i <= @prev_send_time.to_i + 2 sleep 1 end @prev_send_time = t end conn.print message.to_s unless conn.closed? end else nil end end alias << push end # Connection =begin == RICE::Message =end class Message module PATTERN # letter = %x41-5A / %x61-7A ; A-Z / a-z # digit = %x30-39 ; 0-9 # hexdigit = digit / "A" / "B" / "C" / "D" / "E" / "F" # special = %x5B-60 / %x7B-7D # ; "[", "]", "\", "`", "_", "^", "{", "|", "}" LETTER = 'A-Za-z' DIGIT = '\d' HEXDIGIT = "#{DIGIT}A-Fa-f" SPECIAL = '\x5B-\x60\x7B-\x7D' # shortname = ( letter / digit ) *( letter / digit / "-" ) # *( letter / digit ) # ; as specified in RFC 1123 [HNAME] # hostname = shortname *( "." shortname ) SHORTNAME = "[#{LETTER}#{DIGIT}](?:[-#{LETTER}#{DIGIT}\/]*[#{LETTER}#{DIGIT}])?" HOSTNAME = "#{SHORTNAME}(?:\\.#{SHORTNAME})*\\.?" # servername = hostname SERVERNAME = HOSTNAME # nickname = ( letter / special ) *8( letter / digit / special / "-" ) # (nickname starts with DIGIT may occur in split mode) NICKNAME = "[#{LETTER}#{DIGIT}#{SPECIAL}][\-#{LETTER}#{DIGIT}#{SPECIAL}]*" # user = 1*( %x01-09 / %x0B-0C / %x0E-1F / %x21-3F / %x41-FF ) # ; any octet except NUL, CR, LF, " " and "@" USER = '[\x01-\x09\x0B-\x0C\x0E-\x1F\x21-\x3F\x41-\xFF]+' # ip4addr = 1*3digit "." 1*3digit "." 1*3digit "." 1*3digit IP4ADDR = "[#{DIGIT}]{1,3}(?:\\.[#{DIGIT}]{1,3}){3}" H16 = "[#{HEXDIGIT}]{1,4}" # :nodoc: LS32 = "(?:#{H16}:#{H16})|#{IP4ADDR}" # :nodoc: # ip6addr from RFC3986 IP6ADDR = "(?:#{H16}:){6}#{LS32}|" \ "::(?:#{H16}:){5}#{LS32}|" \ "(?:#{H16})?::(?:#{H16}:){4}#{LS32}|" \ "(?:(?:#{H16}:)?#{H16})?::(?:#{H16}:){3}#{LS32}|" \ "(?:(?:#{H16}:){0,2}#{H16})?::(?:#{H16}:){2}#{LS32}|" \ "(?:(?:#{H16}:){0,3}#{H16})?::#{H16}:#{LS32}|" \ "(?:(?:#{H16}:){0,4}#{H16})?::#{LS32}|" \ "(?:(?:#{H16}:){0,5}#{H16})?::#{H16}|" \ "(?:(?:#{H16}:){0,6}#{H16})?::" # hostaddr = ip4addr / ip6addr HOSTADDR = "(?:#{IP4ADDR}|#{IP6ADDR})" # host = hostname / hostaddr HOST = "(?:#{HOSTNAME}|#{HOSTADDR})" # prefix = servername / ( nickname [ [ "!" user ] "@" host ] ) PREFIX = "(?:#{NICKNAME}(?:(?:!#{USER})?@#{HOST})?|#{SERVERNAME})" # nospcrlfcl = %x01-09 / %x0B-0C / %x0E-1F / %x21-39 / %x3B-FF # ; any octet except NUL, CR, LF, " " and ":" NOSPCRLFCL = '\x01-\x09\x0B-\x0C\x0E-\x1F\x21-\x39\x3B-\xFF' # command = 1*letter / 3digit COMMAND = "(?:[#{LETTER}]+|[#{DIGIT}]{3})" # SPACE = %x20 ; space character # middle = nospcrlfcl *( ":" / nospcrlfcl ) # trailing = *( ":" / " " / nospcrlfcl ) # params = *14( SPACE middle ) [ SPACE ":" trailing ] # =/ 14( SPACE middle ) [ SPACE [ ":" ] trailing ] MIDDLE = "[#{NOSPCRLFCL}][:#{NOSPCRLFCL}]*" TRAILING = "[: #{NOSPCRLFCL}]*" PARAMS = "(?:((?: +#{MIDDLE}){0,14})(?: +:(#{TRAILING}))?|((?: +#{MIDDLE}){14}):?(#{TRAILING}))" # crlf = %x0D %x0A ; "carriage return" "linefeed" # message = [ ":" prefix SPACE ] command [ params ] crlf CRLF = '\x0D\x0A' MESSAGE = "(?::(#{PREFIX}) +)?(#{COMMAND})#{PARAMS}\s*(#{CRLF}|\n|\r)" CLIENT_PATTERN = /\A#{NICKNAME}(?:(?:!#{USER})?@#{HOST})\z/on MESSAGE_PATTERN = /\A#{MESSAGE}\z/on end # PATTERN =begin --- RICE::Message::parse(str) =end def self.parse(str) unless PATTERN::MESSAGE_PATTERN =~ str raise InvalidMessage, "Invalid message: #{str.inspect}" else prefix = $1 command = $2 if $3 && $3.size > 0 middle = $3 trailer = $4 elsif $5 && $5.size > 0 middle = $5 trailer = $6 elsif $4 params = [] trailer = $4 elsif $6 params = [] trailer = $6 else params = [] end end params ||= middle.split(/ /)[1..-1] params << trailer if trailer self.build(prefix, command.upcase, params) end =begin --- RICE::Message::build(prefix, command, params) =end def self.build(prefix, command, params) if Command::Commands.include?(command) Command::Commands[command].new(prefix, command, params) elsif Reply::Replies.include?(command) Reply::Replies[command].new(prefix, command, params) else raise UnknownCommand, "unknown command: #{command}" end end =begin --- RICE::Message#prefix --- RICE::Message#command --- RICE::Message#params =end def initialize(prefix, command, params) @prefix = prefix @command = command @params = params end attr_accessor :prefix attr_reader :command, :params =begin --- RICE::Message::#to_s =end def to_s str = '' if @prefix str << ':' str << @prefix str << ' ' end str << @command if @params f = false @params.each do |param| str << ' ' if (param == @params[-1]) && (param.size == 0 || /(^:)|(\s)/ =~ param) str << ':' str << param.to_s f = true else str << param.to_s end end end str << "\x0D\x0A" str end =begin --- RICE::Message::#to_a =end def to_a [@prefix, @command, @params] end def inspect sprintf('#<%s:0x%x prefix:%s command:%s params:%s>', self.class, self.object_id, @prefix, @command, @params.inspect) end end # Message =begin == RICE::Command =end module Command class Command < Message end # Command def self.regist_command cmd eval < # # # $Id: README 198 2009-07-27 10:54:55Z znz $ # * What's this? Nadoka is IRC Client Server program. It's same concept as [[madoka|http://www.madoka.org]]. You can do with this software: - connect to IRC usually - easy to make a bot with ruby * more about See Web pages: - http://www.atdot.net/nadoka/ - http://rubyforge.org/projects/nadoka/ Thank you, -- SASADA Koichi at atdot dot net nadoka-0.7.6/nadoka.rb0000755000000000000000000000424011330676051013246 0ustar rootroot#!/usr/bin/env ruby # ## ## Nadoka: ## Irc Client Server Program ## # # Copyright (c) 2004-2005 SASADA Koichi # # 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: nadoka.rb 226 2010-01-30 00:24:09Z znz $ # Create : K.S. 03/07/10 20:29:07 # unless "".respond_to?(:force_encoding) class String def force_encoding(enc) self end end end $LOAD_PATH.unshift File.dirname(__FILE__) require 'ndk/version' if $0 == __FILE__ require 'optparse' require 'ndk/server' require 'ndk/bot' $stdout.sync=true $NDK_Debug = false unless defined? Process.daemon def Process.daemon(nochdir = nil, noclose = nil) exit!(0) if fork Process.setsid exit!(0) if fork Dir.chdir('/') unless nochdir File.umask(0) unless noclose STDIN.reopen('/dev/null') STDOUT.reopen('/dev/null', 'w') STDERR.reopen('/dev/null', 'w') end end end rcfile = nil daemon = false optparse = OptionParser.new{|opts| opts.banner = "Usage: ruby #{$0} [options]" opts.separator "" opts.separator "Require options:" opts.on("-r", "--rc [RCFILE]", "Specify rcfile(required)"){|f| rcfile = f } opts.separator "" opts.separator "Optional:" opts.on("-d", "--debug", "Debug Nadoka"){ $NDK_Debug = true $DEBUG = true puts 'Enter Nadoka Debug mode' } opts.on("--daemon", "run as daemon"){ daemon = true } opts.separator "" opts.separator "Common options:" opts.on_tail("-h", "--help", "Show this message"){ puts Nadoka.version puts opts exit } opts.on_tail("-v", "--version", "Show version"){ puts Nadoka.version } } optparse.parse!(ARGV) unless rcfile puts Nadoka.version puts optparse exit end if daemon Process.daemon end begin GC.start Nadoka::NDK_Server.new(rcfile).start rescue Nadoka::NDK_QuitProgram # rescue Nadoka::NDK_RestartProgram GC.start ObjectSpace.each_object(Socket) {|sock| sock.close} retry rescue Exception => e open('nadoka_fatal_error', 'w'){|f| f.puts e f.puts e.backtrace.join("\n") } end end nadoka-0.7.6/ChangeLog0000644000000000000000000011024311640616724013241 0ustar rootroot# ChangeLog of Nadoka($Rev: 256 $) # $Id: ChangeLog 256 2011-09-28 13:15:00Z znz $ 2011-09-28 Kazuhiro NISHIYAMA * plugins/translatebot.nb: merge patch from ko1_ndk. support Bing Translator. 2011-09-15 Kazuhiro NISHIYAMA * plugins/opensearchbot.nb: split class and enabled to run on command line for tests. 2011-08-16 Kazuhiro NISHIYAMA * plugins/googlebot.nb: bot return uri only, because cannot fetch results from Google code search. 2011-08-15 Kazuhiro NISHIYAMA * plugins/opensearchbot.nb: add new bot. 2011-06-02 Kazuhiro NISHIYAMA * rice/irc.rb (RICE::Message::PATTERN::NICKNAME): nickname starts with DIGIT may occur in split mode. 2010-12-14 Kazuhiro NISHIYAMA * plugins/tenkibot.nb: use same_bot? and clean up trailing spaces. 2010-12-14 Kazuhiro NISHIYAMA * plugins/tenkibot.nb (TenkiBot#tenki): cut after "...". (TenkiBot#bot_initialize): use bot_init_utils. 2010-12-14 Kazuhiro NISHIYAMA * ndk/bot.rb (Nadoka::NDK_Bot#bot_init_utils): add new methods. * plugins/googlebot.nb (GoogleBot#bot_initialize): use bot_init_utils. 2010-10-21 Kazuhiro NISHIYAMA * plugins/googlebot.nb: add ch_kcode. merge patch from ko1_ndk. 2010-10-21 Kazuhiro NISHIYAMA * ndk/server.rb (Nadoka::NDK_Server#server_main_proc): do not use nick_succ when channel and retry join. 2010-10-21 Kazuhiro NISHIYAMA * plugins/translatebot.nb: added (contributed from SASADA Koichi) 2010-09-29 Kazuhiro NISHIYAMA * nadokarc, plugins/autoawaybot.nb, plugins/autodumpbot.nb, plugins/backlogbot.nb, plugins/dictbot.nb, plugins/modemanager.nb, plugins/pastebot.nb, plugins/roulettebot.nb, plugins/timestampbot.nb, plugins/xibot.nb, rice/irc.rb: CRLF -> LF. 2010-09-29 Kazuhiro NISHIYAMA * 0.7.5: released * ndk/version.rb: 0.7.6 2010-09-28 Kazuhiro NISHIYAMA * plugins/googlebot.nb: add gime and gimed. * plugins/googlebot.nb: use shortest match. 2010-09-13 Kazuhiro NISHIYAMA * plugins/dictbot.nb: use shortest match. 2010-09-06 NARUSE, Yui * plugins/twitterbot.nb: added. * plugins/xibot.nb: added. * plugins/backlogbot.nb: send message which matches pattern. 2010-08-18 Kazuhiro NISHIYAMA * rice/irc.rb: applied nurse's patch. Update regexp of IPv6 address based on RFC3986. 2010-07-21 Kazuhiro NISHIYAMA * nadokarc: update IPv6 Sample in Servers. see http://www.ircnet.ne.jp/ 2010-02-15 Kazuhiro NISHIYAMA * plugins/rss_checkbot.nb (RSS_CheckBot#send_notice): all #send_notice should support Array in @ch. * rice/irc.rb (RICE::Message#to_s): add recursive #to_s to avoid "can't convert Array into String (TypeError)" in `<<'. 2010-01-28 Kazuhiro NISHIYAMA * ndk/server.rb (Nadoka::NDK_Server#set_signal_trap): merge patch from n0kada. http://www.atdot.net/sp/view/dswxwk 2010-01-25 Kazuhiro NISHIYAMA * nadokarc: update Servers. see http://www.wide.ad.jp/news/press/20100125-IRC-server-close-j.html and http://www.ircnet.ne.jp/ . 2010-01-01 Koichi Sasada * plugins/googlebot.nb: support new searcher calc and code. * plugins/googlebot.nb: support abbreviated syntax (ex: g> ). 2009-11-30 Kazuhiro NISHIYAMA * nadokarc: update Servers. see http://twitter.com/ircnet_fujisawa/status/6185537849 for irc6.fujisawa.wide.ad.jp, and http://en.wikipedia.org/wiki/IRCd says most often used port for SSL is 6697. 2009-11-08 Kazuhiro NISHIYAMA * nadokarc: update Servers. see http://slashdot.jp/it/article.pl?sid=09/11/06/0747210 2009-10-23 Kazuhiro NISHIYAMA * plugins/googlebot.nb: fallback to json gem. 2009-09-22 Kazuhiro NISHIYAMA * ndk/server.rb (Nadoka::NDK_Server#server_main_proc): support RPL_ISUPPORT. 2009-09-22 Kazuhiro NISHIYAMA * ndk/error.rb: add Nadoka::NDK_InvalidMessage. * ndk/server.rb (Nadoka::NDK_Server#send_to_server): check [\r\n]. 2009-09-21 Kazuhiro NISHIYAMA * rice/irc.rb: remove magic comment. ruby 1.9.2 works without it. 2009-09-21 Kazuhiro NISHIYAMA * 0.7.2: released * ndk/version.rb: bumped version to 0.7.5. 2009-09-17 Kazuhiro NISHIYAMA * plugins/googlebot.nb: use hl because lr used by web searcher only. 2009-09-17 Kazuhiro NISHIYAMA * plugins/googlebot.nb: support more searcher of Google AJAX Search API. 2009-09-17 Kazuhiro NISHIYAMA * plugins/googlebot.nb: use Google AJAX Search API. see http://code.google.com/intl/ja/apis/ajaxsearch/ for detail. 2009-09-14 Kazuhiro NISHIYAMA * plugins/googlebot.nb: Google SOAP Search API (No Longer Available). see http://code.google.com/intl/ja/apis/soapsearch/ and http://googlecode.blogspot.com/2009/08/well-earned-retirement-for-soap-search.html 2009-08-21 Kazuhiro NISHIYAMA * nadoka.rb, ndk/server.rb: revert previous change, and use ?\x1 instead. 2009-08-21 Kazuhiro NISHIYAMA * nadoka.rb, ndk/server.rb: handle ctcp on ruby 1.9. 2009-08-18 Kazuhiro NISHIYAMA * nadoka.rb, ndk/config.rb, rice/irc.rb: ruby 1.9 support (not completed yet). apply patch from unak. 2009-08-17 Kazuhiro NISHIYAMA * nadokarc, ndk/config.rb, ndk/server.rb: no listen when Client_server_port is nil. 2009-08-17 Kazuhiro NISHIYAMA * plugins/tenkibot.nb: omit empty min celsius. 2009-07-29 Kazuhiro NISHIYAMA * lib/rss_check.rb: update lirs_uri. 2009-07-29 Kazuhiro NISHIYAMA * 0.7.1 : released * ndk/version.rb : 0.7.2 2009-07-27 Kazuhiro NISHIYAMA * nadoka.rb: add --daemon option. (apply patch from unak) 2009-07-04 Kazuhiro NISHIYAMA * plugins/dictbot.nb: avoid nkf bug. add waei as w, ruigo as r. new yahoo dict URI. (apply patch from ko1_ndk) * plugins/googlebot.nb: quote splited words. (apply patch from ko1_ndk) 2009-07-01 Kazuhiro NISHIYAMA * rice/irc.rb: @conn[0] may be nil. 2009-05-25 Kazuhiro NISHIYAMA * ndk/config.rb, ndk/server.rb: add Primitive_Filter to filter any commands. (apply patch from unak) 2009-05-20 Kazuhiro NISHIYAMA * plugins/identifynickserv.nb: added. 2009-05-20 Kazuhiro NISHIYAMA * ndk/bot.rb, ndk/server.rb: add on_server_connected. 2009-04-16 Kazuhiro NISHIYAMA * nadokarc, ndk/config.rb, ndk/server.rb, rice/irc.rb: support SSL. Wed Mar 19 19:46:52 2008 Kazuhiro NISHIYAMA * ndk/logger.rb : pass ch to msgobj from clog. * ndk/config.rb : unify safe channel log filename. Mon Jul 23 01:07:08 2007 Kazuhiro NISHIAYMA * lib/rss_check.rb : bug fix when dc_date is nil. * ndk/config.rb : fix bug when non-Hash in BotConfig array. Mon Jul 02 19:01:04 2007 Koichi Sasada * plugins/tenkibot.nb : fix output format detail. * plugins/googlebot.nb : fix to retry 5 times if error is occurred while google search. * plugins/dictbot.nb : fix to show short summary. 2007-07-02(Mon) 18:01:58 +0900 Kazuhiro NISHIAYMA * rice/irc.rb : allow trailing "." in HOSTNAME (for NickServ on Freenode) 2007-02-20(Tue) 23:35:41 +0900 Kazuhiro NISHIAYMA * **/*.rb, plugins/*.nb : fix typos 2006-10-28(Sat) 22:08:11 +0900 Kazuhiro NISHIAYMA * ndk/server_state.rb : fix bug on_mode after server split 2006-10-20(Fri) 22:22:07 +0900 Kazuhiro NISHIAYMA * ndk/bot.rb : new method ccn2rcn 2006-10-07(Sat) 03:52:11 +0900 Koichi Sasada * plugin/tenkibot.nb : added 2006-10-02(Mon) 21:58:40 +0900 Koichi Sasada * 0.7.0 : released * ndk/version.rb : 0.7.1 2006-10-02(Mon) 21:41:24 +0900 Koichi Sasada * plugins/weatherbot.nb : removed * plugins/sendpingbot.nb : added * ndk/client.rb : modified for pingbot * ndk/server.rb : ditto 2006-09-01(Fri) 02:04:15 +0900 Kazuhiro NISHIAYMA * ndk/bot.rb : support send to a safe channel * nadokarc : fix misalignment 2006-08-31(Thu) 01:40:39 +0900 Kazuhiro NISHIAYMA * ndk/bot.rb : fix typos 2006-08-11(Fri) 14:45:01 +0900 Koichi Sasada * plugins/gonzuibot.nb : add a comma * plugins/pastebot.nb : add nick to paste page name * plugins/googlebot.nb : fix show_char_code_and_erace_tag 2006-05-16(Tue) 16:16:30 +0900 Koichi Sasada * ndk/server.rb : handle TERM signal 2006-03-27(Mon) 19:50:27 +0900 Koichi Sasada * plugins/dictbot.nb : fix dict index * plugins/roulettebot.nb : added * rice/irc.rb : loosen nick regexp * ndk/server.rb : randomize server if serching next server 2005-09-30(Fri) 00:05:19 +0900 Koichi Sasada * plugins/googlebot.nb : fix bugs * plugins/dictbot.nb : support * plugins/gonzuibot.nb : added * ndk/config.rb : fix bugs 2005-06-25(Sat) 19:02:24 +0900 Koichi Sasada * nadoka.rb : output error information to file 'nadoka_fatal_error' when unhandled error is occure (bug) 2005-06-14(Tue) 21:26:07 +0900 Koichi Sasada * ndk/server.rb : change reloaded message output method to slog 2005-06-13(Mon) 18:25:33 +0900 Koichi Sasada * nadokarc, ndk/logger.rb, ndk/config.rb : support :channel_name_in_file_name option for logging 2005-06-13(Mon) 00:11:39 +0900 Koichi Sasada * ndk/server_state.rb : fix typo * ndk/config.rb : see ENV['LANG'] to decide filename encoding * ndk/version.rb : fix version format * ndk/bot.rb : fix comment 2005-06-10(Fri) 04:38:25 +0900 Koichi Sasada * ndk/server.rb : fix logic operation 2005-06-09(Thu) 17:22:25 +0900 Koichi Sasada * ndk/logger.rb : synchronize IOLogWriter output 2005-06-09(Thu) 17:18:21 +0900 Koichi Sasada * ndk/config.rb : set default file encoding to EUC 2005-06-09(Thu) 05:11:58 +0900 Koichi Sasada * ndk/config.rb : fix to get FilenameEncoding * ndk/server.rb : stop away message until server connection 2005-06-08(Wed) 07:56:17 +0900 Koichi Sasada * ndk/config.rb : clear if channel setting is not hash 2005-06-08(Wed) 07:42:17 +0900 Koichi Sasada * plugins/googlebot.nb : fix to use count default language 2005-06-08(Wed) 06:21:25 +0900 Koichi Sasada * ndk/server.rb : trap IOError and change error output * ndk/config.rb : support {prefix:(nick|user|host)} specifier * ndk/logger.rb : {user} -> {nick} * nadokarc : apply above changes * rice/irc.rb : output unless port is closed * plugins/backlogbot.nb : change spell (@store -> @stores) 2005-06-05(Sun) 22:47:15 +0900 Koichi Sasada * ndk/server.rb : PONG MISS message dlog -> slog 2005-05-31(Tue) 14:08:26 +0900 Koichi Sasada * nadokarc : change log setting of "#nadoka_check" 2005-05-29(Sun) 14:01:02 +0900 Koichi Sasada * ndk/server.rb, ndk/server_state.rb, ndk/logger.rb : NICK and QUIT logging from server_state * nadokarc, ndk/config.rb : change default PART, QUIT message format 2005-05-29(Sun) 12:53:48 +0900 Koichi Sasada * plugins/weba.nb : set default password on * ndk/logger.rb : fix nick logging 2005-05-29(Sun) 07:06:55 +0900 Koichi Sasada * plugins/weba.nb : use logger's message store * ndk/logger.rb, ndk/config.rb : some changes for weba * nadokarc : add vim pragma and FilenameEncoding setting * ndk/client.rb, ndk/server.rb : fix logger relaod process * ndk/server_state.rb : add channel_raw_names 2005-05-29(Sun) 03:35:28 +0900 Koichi Sasada * nadokarc : add BackLogBot as default bot 2005-05-29(Sun) 03:31:07 +0900 Koichi Sasada * ndk/server.rb : add @rc read accessor * nadokarc, ndk/config.rb : support default setting_name as rc file name 2005-05-29(Sun) 03:04:53 +0900 Koichi Sasada * ndk/config.rb : support filename encoding(utf-8) 2005-05-29(Sun) 02:49:12 +0900 Koichi Sasada * ndk/server_state.rb, ndk/config.rb, ndk/logger.rb : new logging scheme is added * nadokarc : add indent and log detail setting * plugins/backlogbot.nb : use logger's message store 2005-05-27(Fri) 21:32:35 +0900 Koichi Sasada * nadokarc : comment out Quit_Message and change Default_log, Log_TimeFormat setting * ndk/client.rb : fix welcome message * ndk/config.rb : change Default_log, System_log path, Log_TimeFormat and add Log_MessageFormat. 2005-05-26(Thu) 22:11:48 +0900 Koichi Sasada * plugins/autodumpbot.nb, plugins/sixamobot.nb : change copyright year 2005-05-26(Thu) 22:07:15 +0900 Koichi Sasada * ndk/config.rb : fix a bug 2005-05-26(Thu) 21:51:10 +0900 Koichi Sasada * nadokarc : change default quit message 2005-05-26(Thu) 21:45:49 +0900 Koichi Sasada * nadokarc : fix ACL comment 2005-05-26(Thu) 21:42:29 +0900 Koichi Sasada * plugins/backlogbot.nb : fix configuration example 2005-05-26(Thu) 21:24:50 +0900 Koichi Sasada * log/.deleteme : remove 2005-05-26(Thu) 21:23:03 +0900 Koichi Sasada * plugins/weatherbot.nb : add parentheses * plugins/rss_checkbot.nb : add configuration :over_message 2005-05-26(Thu) 21:03:24 +0900 Koichi Sasada * some files : change copyright year 2005-05-26(Thu) 20:52:32 +0900 Koichi Sasada * plugins/googlebot.nb : fix plural support bug 2005-05-26(Thu) 20:27:16 +0900 Koichi Sasada * plugins/googlebot.nb : fix configuration example and support plural * plugins/rss_checkbot.nb : change bot_state format and separate rss check function * lib/rss_check.rb : return failed information if exception is raised 2005-05-26(Thu) 11:21:31 +0900 Koichi Sasada * ndk/bot.rb : some refactoring 2005-05-26(Thu) 10:53:40 +0900 Koichi Sasada * plugins/autoawaybot.nb : add configuration example and some refactoring 2005-05-26(Thu) 10:44:33 +0900 Koichi Sasada * plugins/timestampbot.nb : add :client configuration (if true, output timestamp to clients. default: false) 2005-05-26(Thu) 10:37:29 +0900 Koichi Sasada * plugins/timestampbot.nb : fix configuration example 2005-05-26(Thu) 10:29:16 +0900 Koichi Sasada * plugins/message.nb => plugins/messagebot.nb * plugins/autoaway.nb => plugins/autoawaybot.nb * plugins/autodump.nb => plugins/autodumpbot.nb * plugins/sixamo.nb => plugins/sixamobot.nb * plugins/mailcheck.nb => plugins/mailcheckbot.nb * plugins/sixamobot.nb : fix configuration example 2005-05-26(Thu) 10:18:18 +0900 Koichi Sasada * plugins/rss_check.nb => plugins/rss_checkbot.nb, * plugins/cron.nb => plugins/cronbot.nb * plugins/mailcheck.nb, plugins/modemanager.nb, plugins/weba.nb, plugins/drbot.nb, plugins/rss_checkbot.nb : fix configuration example 2005-05-26(Thu) 02:35:09 +0900 Koichi Sasada * ndk/version.rb : 0.7.0 2005-05-25(Wed) 14:22:32 +0900 Koichi Sasada * ndk/config.rb, nadokarc : change bot configuration format (BotConfig format) * ndk/config.rb : Nadoka::require is supported (if reload configuration, required files by Nadoka::require are cleared) * ndk/bot.rb : BotClass (Array) => BotClasses (Hash (name => body)) * plugins/googlebot.nb : fix setting description and fix google_key file path search logic * plugins/dict_bot.nb => plugins/dictbot.nb * plugins/backlogbot.nb : fix setting description 2005-05-22(Sun) 23:40:28 +0900 Koichi Sasada * plugins/dict_bot.nb : /dic(.)>.../ => /\Adic(.)>.../ * plugins/googlebot.nb : remove words in results * plugins/pastebot.nb : support fpaste 2005-05-22(Sun) 23:28:53 +0900 Koichi Sasada * ndk/logger.rb : support directory auto generation 2005-05-22(Sun) 01:53:38 +0900 Koichi Sasada * nadoka/ : mkdir * lib/ : mkdir * ndk_*.rb : move to nadoka/ and change require path * tagparts.rb, rss_check.rb : move to lib/ and change require path * ndk_manager.rb (NDK_Manager) : move to ndk/server.rb and rename NDK_Server * ndk_err.rb : move to ndk/error.rb 2005-03-15(Tue) 17:30:25 +0900 Koichi Sasada * ndk_state.rb : re-fix state management 2005-03-04(Fri) 16:28:18 +0900 Koichi Sasada * 0.6.4 released * ndk_version.rb : 0.6.5 2005-03-04(Fri) 13:23:37 +0900 Koichi Sasada * ndk_state.rb : fix server message pattern 2005-01-20(Thu) 23:15:33 +0900 Koichi Sasada * nadokarc : fix typo 2004-12-20(Mon) 20:15:03 +0900 Koichi Sasada * plugins/weatherbot.nb : change unkown region error message and ignore region's case 2004-12-20(Mon) 20:04:53 +0900 Koichi Sasada * plugins/weatherbot.nb : strip region name 2004-12-20(Mon) 19:53:18 +0900 Koichi Sasada * plugins/dict_bot.nb, plugins/pastebot.nb, plugins/weatherbot.nb added (test version) 2004-12-03(Fri) 14:45:19 +0900 Koichi Sasada * ndk_state.rb : fix again (patched by U.Nakamura) 2004-12-03(Fri) 13:59:48 +0900 Koichi Sasada * ndk_state.rb : fix bug about mode change by server * plugins/modemanager.rb : fix bug to detect mode 2004-11-24(Wed) 13:20:17 +0900 Koichi Sasada * plugins/autodump.nb : fix bug (apply patch from shugo) 2004-08-25(Wed) 15:00:49 +0900 Koichi Sasada * nadokarc : fix bug(add ',') 2004-08-20(Fri) 18:48:36 +0900 Koichi Sasada * plugins/googlebot.nb : fix googlec prompt 2004-08-19(Thu) 01:17:13 +0900 Koichi Sasada * nadokarc : add '#nadoka' channel setting 2004-08-19(Thu) 01:12:03 +0900 Koichi Sasada * plugins/autoaway.nb : fix bug(add condition at on_timer) 2004-08-16(Mon) 13:07:15 +0900 Koichi Sasada * ndk_state.rb, ndk_manager.rb : fix kick bug([nadoka:271]) 2004-08-14(Sat) 21:24:28 +0900 Koichi Sasada * plugins/modemanager.nb : fix mode check routine(match -> include?) 2004-08-13(Fri) 11:04:36 +0900 Koichi Sasada * 0.6.3 released * ndk_version.rb : 0.6.4 2004-08-13(Fri) 10:18:03 +0900 Koichi Sasada * plugins/modemanager.nb : fix bug * plugins/autodump.nb : added * plugins/*.nb : fix hash access([] to fetch(key, default)). 2004-08-13(Fri) 10:04:26 +0900 Koichi Sasada * ndk_state.rb : fix enbug * nadokarc : change default channel and fix typo * plugins/drbot.nb : fix configuration example's bug * plugins/sixamo.nb : fix typo 2004-08-05(Thu) 08:25:48 +0900 Koichi Sasada * ndk_state.rb : fix some bugs 2004-08-02(Mon) 07:51:51 +0900 Koichi Sasada * ndk_client.rb : fix pong message field * ndk_state.rb : fix on_quit process * plugins/modemanager.nb : don't set mode if already set 2004-07-28(Wed) 05:07:00 +0900 Koichi Sasada * plugins/google.nb : support gooo*gle 2004-07-27(Tue) 11:16:05 +0900 Koichi Sasada * plugins/ndk_client.rb : fix typo 2004-07-26(Mon) 02:13:58 +0900 Koichi Sasada * plugins/backlogbot.nb : fixed a bug * plugins/modemanager.nb : applied unak's patch (support delaied distribution, and so on) * ndk_client.rb : fix exception handling 2004-07-24(Sat) 21:26:30 +0900 Koichi Sasada * plugins/modemanager.nb : added 2004-07-24(Sat) 07:30:10 +0900 Koichi Sasada * ndk_manager.rb : fix server login process(reconnect if server replies error) * ndk_manager.rb, ndk_state.rb : support 'KICK' state * ndk_manager.rb, ndk_staet.rb : support safe channel * plugins/backlogbot.nb : fix around safe channel bugs * plugins/googlebot.nb(googlec) : fix charcode problem 2004-07-21(Wed) 19:17:13 +0900 Koichi Sasada * ndk_bot.rb(canonical_channel_name) : added and this alias 'ccn' is added * ndk_state.rb : rename channel_member to channel_users and channel_member_mode to channel_user_mode 2004-07-21(Wed) 00:57:44 +0900 Koichi Sasada * ndk_manager.rb : part only from own nick when server disconnect 2004-07-20(Tue) 11:33:46 +0900 Koichi Sasada * 0.6.2 release * ndk_version.rb : 0.6.3 2004-07-20(Tue) 04:06:17 +0900 Koichi Sasada * ndk_client.rb : use NdkCommandDescription to list items * ndk_config.rb(identical_channel_name) : add euc-jp option to regexp 2004-07-20(Tue) 02:23:35 +0900 Koichi Sasada * plugins/sixamo.nb : change talk ratio algorithm 2004-07-20(Tue) 02:23:35 +0900 Koichi Sasada * plugins/sixamo.nb : fix talk ratio algorithm 2004-07-20(Tue) 02:18:01 +0900 Koichi Sasada * plugins/sixamo.nb : down talk rate and memorize his talk 2004-07-20(Tue) 02:00:45 +0900 Koichi Sasada * ndk_logger.rb : synchronize IO object output 2004-07-20(Tue) 01:48:45 +0900 Koichi Sasada * ndk_manager.rb : fix invoke time event timing 2004-07-20(Tue) 00:37:02 +0900 Koichi Sasada * ndk_manager.rb : add @pong_fail_count and MAX_PONG_FAIL to check server response * ndk_client.rb : fix usage 100% problem(remove Thread.pass and ignore message when server connection is closed) 2004-07-19(Mon) 22:10:05 +0900 Koichi Sasada * 0.6.1 releaseds * ndk_version.rb : 0.6.2 2004-07-19(Mon) 21:52:27 +0900 Koichi Sasada * ndk_manager.rb : TimerIntervalSec to 60 2004-07-19(Mon) 21:40:17 +0900 Koichi Sasada * ndk_manager.rb : fix time thread timing * ndk_manager.rb : fix SIGINT hook * ndk_version.rb : remove debug code 2004-07-19(Mon) 21:00:21 +0900 Koichi Sasada * 0.6.0 released * ndk_version.rb : 0.6.1 2004-07-19(Mon) 20:59:11 +0900 Koichi Sasada * plugins/mailcheck.nb : updated from http://cvs.sourceforge.jp/cgi-bin/viewcvs.cgi/ruexli/nadoka-plugins/ 2004-07-19(Mon) 19:50:40 +0900 Koichi Sasada * rice/irc.rb : support long nick * ndk_manager.rb : fix time event invoke timing * ndk_logger.rb(slog, clog, write_log) : add nostamp option * plugins/timestampbot.nb 2004-07-17(Sat) 00:06:54 +0900 Koichi Sasada * plugins/googlebot.nb : fix char code bugs * ndk_manager.rb : trap SIGINT to quit program * ndk_client.rb : add exception hook to protect unexpected disconnect 2004-07-14(Wed) 04:29:47 +0900 Koichi Sasada * ToDo : removed * README : fix url 2004-07-14(Wed) 04:27:53 +0900 Koichi Sasada * ndk_manager.rb : fix exception handling 2004-07-14(Wed) 04:03:27 +0900 Koichi Sasada * ndk_config.rb : add DefaultBotFiles * ndk_config.rb : ch_config(ch, key) is added * ndk_config.rb(make_bot_instance) : logging message to bot_state * ndk_manager.rb : fix error recovery process(set @connected to false) * plugins/backlogbot.nb : fix some bugs (user to user messages and so on) * plugins/weba.nb : fix configuration example's bug 2004-07-12(Mon) 16:51:24 +0900 Koichi Sasada * ndk_version.rb : add rev to version string if this in trunk 2004-07-12(Mon) 16:39:59 +0900 Koichi Sasada * ChangeLog : add rev keyword 2004-07-11(Sun) 11:37:14 +0900 Koichi Sasada * plugins/backlogbot.nb : fix schemes 2004-07-10(Sat) 22:48:35 +0900 Koichi Sasada * nadokarc : fix filenameencoding bugs * ndk_logger.rb : fix user to user message's logname 2004-07-10(Sat) 21:22:18 +0900 Koichi Sasada * ndk_manager.rb : ignore NOTICE messages when logged in * plugins/backlogbot.rb : ignore non channel messages 2004-07-10(Sat) 17:33:10 +0900 Koichi Sasada * ndk_version.rb : check trunk * ndk_bot.rb : add on_quit_from_channel event * plugins/backlogbot.nb: added 2004-07-09(Fri) 07:45:29 +0900 Koichi Sasada * plugins/drbot.nb : rename method name kick_* -> send/recv... 2004-07-09(Fri) 03:52:01 +0900 Koichi Sasada * plugins/drbot.nb, plugins/drbcl.rb : added * plugins/mailcheck.nb, plugins/message.nb :added (contributed from Kazuhiro NISHIYAMA) 2004-07-09(Fri) 01:39:01 +0900 Koichi Sasada * ndk_client.rb(client_notice) : added * ndk_manager.rb, ndk_client.rb : support '/nadoka status' command and add NDK_Manager#ndk_state, NDK_Bot#bot_state, NDK_Client#state * plugins/rss_check.nb(bot_state) : added 2004-07-08(Thu) 08:31:33 +0900 Koichi Sasada * tagparts.rb : add tree2string 2004-07-07(Wed) 17:53:27 +0900 Koichi Sasada * ndk_client.rb : fix socket bugs 2004-07-07(Wed) 17:15:07 +0900 Koichi Sasada * plugins/weba.nb : add basic authentication, and some fixs * plugins/shellbot.nb, plugins/evalbot.nb : undo 2004-07-07(Wed) 16:04:30 +0900 Koichi Sasada * plugins/shellbot.nb, plugins/evalbot.nb : fix bugs around constant search path * ndk_state.rb(channel_member_mode) : added * ndk_bot.rb : remove @raw_prefix 2004-07-07(Wed) 10:18:51 +0900 Koichi Sasada * plugins/weba.nb : fix some issues 2004-07-06(Tue) 22:38:58 +0900 Koichi Sasada * ndk_client.rb : fix exception bug * ndk_manager.rb, ndk_config.rb : fix reload process * nadokarc : fix typo * plugins/weba.nb : fix some bugs, support writing * tagparts.rb : added(required by weba) 2004-07-05(Mon) 15:40:09 +0900 Koichi Sasada * ndk_config.rb : change default Channel_Info, :timing as :startup * ndk_manager.rb : fix nick change bugs * plugins/weba.nb : added(test version) 2004-07-05(Mon) 13:13:26 +0900 Koichi Sasada * ndk_manager.rb : add execption hook(client thread) 2004-07-05(Mon) 09:43:12 +0900 Koichi Sasada * nadokarc : fix bug 2004-07-05(Mon) 08:31:35 +0900 Koichi Sasada * ndk_logger.rb : ignore IOError when logging * plugins/googlebot.nb : add option ':googlec_maxwords' 2004-07-05(Mon) 08:02:43 +0900 Koichi Sasada * nadokarc : auto detect filename encoding(carelessly) 2004-07-05(Mon) 07:28:17 +0900 Koichi Sasada * nadokarc, ndk_config.rb : add 'Mode' option and Channel_Info[:initial_mode] option * nadokarc, ndk_config.rb : add 'FilenameEncoding' option * ndk_logger.rb, ndk_config.rb : support japanese character encoding channel name * ndk_config.rb : fix canonical_channel_name and add identical_channel_name * ndk_manager.rb : support "/nadoka reconnect" command * ndk_manager.rb : ping to server and check this response, due to check connection * ndk_manager.rb, ndk_state.rb : fix behavior when server connection is closed 2004-07-04(Sun) 17:50:06 +0900 Koichi Sasada * ndk_logger.rb : synchronize file output 2004-07-04(Sun) 16:15:24 +0900 Koichi Sasada * plugins/googlebot.nb : fix character code bugs 2004-07-03(Sat) 03:25:24 +0900 Koichi Sasada * plugins/googlebot.nb : added * nadokarc : add default join channel '#nadoka_check' 2004-06-01(Tue) 16:51:29 +0900 Koichi Sasada * plugins/checkbot.nb : added(incomplete) * plugins/rss_check.nb : change notice format * ndk_client.rb : fix login messages(commands) 2004-05-14(Fri) 02:36:04 +0900 Koichi Sasada * ndk_manager.rb : fix part process at enter_away 2004-05-14(Fri) 02:27:06 +0900 Koichi Sasada * ndk_config.rb(canonical_channel_name) : added and all configuration keys of channel information are canonical * ndk_state.rb(canonical_channel_name) : added and all state keys of channel are canonical 2004-05-09(Sun) 18:11:30 +0900 Koichi Sasada * ndk_manager.rb : correct command to clients 2004-05-09(Sun) 17:27:46 +0900 Koichi Sasada * ndk_manager.rb : change away nick lag time to 2 sec 2004-05-09(Sun) 16:46:30 +0900 Koichi Sasada * rice/irc.rb : fix some regexp bugs * ndk_client.rb(send_motd) : use client original nick * ndk_client.rb ; check io is still open * plugins/rss_check.nb : change string compare operator from '=~' to '==' 2004-05-09(Sun) 03:33:29 +0900 Koichi Sasada * ndk_manager.rb, ndk_state.rb(nick_succ) : change nick.succ algorithm and timing(always change) * ndk_manager.rb : refactoring around nick and away nick * ndk_config.rb : change login timing action default to do nothing 2004-05-05(Wed) 17:19:14 +0900 Koichi Sasada * ndk_version.rb : 0.5.5 prepared 2004-05-05(Wed) 16:24:03 +0900 Koichi Sasada * ChangeLog : change date format * 0.5.4 released Sun, 02 May 2004 03:34:54 +0900 Koichi Sasada * nadoka.rb : add location of nadoka.rb to load path Sat, 01 May 2004 23:58:55 +0900 Koichi Sasada * ndk_manager.rb : support nadoka control with signal and bot event on_sigusr[12] Sat, 01 May 2004 23:04:40 +0900 Koichi Sasada * ndk_manager.rb : fix typo Sat, 01 May 2004 19:11:29 +0900 Koichi Sasada * change source management system to Subversion Sat, 01 May 2004 14:38:04 +0900 Koichi Sasada * ndk_manager.rb : fix bootstrap process * rice/irc.rb : add easy flood control * 0.5.3 released Sat, 01 May 2004 14:03:52 +0900 Koichi Sasada * nadokarc : fix typo Sat, 01 May 2004 13:24:14 +0900 Koichi Sasada * ndk_config.rb : add nadoka server name * nadokarc : fix IRCnet servers * 0.5.2 released Sat, 01 May 2004 06:35:28 +0900 Koichi Sasada * ndk_logger.rb : added * ndk_bot.rb : add @bot_config * ndk_* : use @logger instead of @config to logging * ndk_config.rb : extend BotConfig format * ndk_manager.rb, rice/irc.rb : fix reconnect process * some refactoring Fri, 30 Apr 2004 02:57:46 +0900 Koichi Sasada * ndk_manager.rb : ignore(and report) exception while '/nadoka reload' reloading Wed, 28 Apr 2004 11:24:23 +0900 Koichi Sasada * ndk_err.rb : change exception hierarchy * ndk_manager.rb : dup filtered message Wed, 28 Apr 2004 10:31:29 +0900 Koichi Sasada * 0.5.1 released Wed, 28 Apr 2004 02:37:34 +0900 Koichi Sasada * plugins/shellbot.nb : remove debug print Wed, 28 Apr 2004 02:07:06 +0900 Koichi Sasada * plugins/evalbot.nb, plugins/shellbot.nb : added * ndk_bot.rb : add on_client_privmsg, on_nadoka_command * ndk_err.rb : add some exception class * ndk_manager.rb : fix some bugs Tue, 27 Apr 2004 22:29:07 +0900 Koichi Sasada * ndk_version.rb : added * ndk_manager.rb : add Privmsg_Filter, Notice_Filter Mon, 26 Apr 2004 10:46:49 +0900 Koichi Sasada * 0.5.0 released Mon, 26 Apr 2004 10:29:47 +0900 Koichi Sasada * ndk_manager.rb : change name on_message_default to on_every_message * ndk_manager.rb, ndk_bot.rb : change first argument of bot event handlers to PrefixObject Sun, 25 Apr 2004 22:59:07 +0900 Koichi Sasada * plugins/rss_check.nb : add on_privmsg * ndk_manager.rb : make ndk_error message more detail Sun, 25 Apr 2004 20:47:16 +0900 Koichi Sasada * ndk_manager.rb, ndk_bot.rb : fix typo(bot_destruct) * ndk_err.rb : add NDK_BotBreak * ndk_state.rb : remove debug output * plugins/rss_check.rb : exception handling when translation error Sun, 25 Apr 2004 18:21:30 +0900 Koichi Sasada * ndk_manager.rb : add on_message_default Sun, 25 Apr 2004 14:55:56 +0900 Koichi Sasada * rss_check.rb : fix recent check Sat, 24 Apr 2004 23:48:07 +0900 Koichi Sasada * ndk_manager.rb : fix bug TCPServer.new. reported by Tadaaki OKABE . * ndk_config.rb : fix ndk_config.rb script. * rss_check.rb : add LIRS support * rss_check.nb : add space around uri Sat, 24 Apr 2004 13:07:22 +0900 Koichi Sasada * rss_check.rb, plugins/rss_check.nb : added Sat, 24 Apr 2004 02:37:47 +0900 Koichi Sasada * ndk_config.rb : fix to use string strip Sat, 24 Apr 2004 01:20:55 +0900 Koichi Sasada * ndk_manager.rb, etc : introduce invoke_event mechanism * ndk_config.rb : support Quit_Message which showed when program is quitting * ndk_config.rb : support Channel_info{"ch" => {:part_message => '...'}} * ndk_manager.rb, etc : fix exception handling with thread * ndk_manager.rb : support /nadoka restart Fri, 23 Apr 2004 22:27:17 +0900 Koichi Sasada * ndk_state.rb : remove debug print Fri, 23 Apr 2004 19:56:54 +0900 Koichi Sasada * support tracing channel state. so nadoka doesn't need to command NAMES when a client join * ndk_manager.rb : support channel join when client login (:timing => :login) by U.Nakamura * ndk_config.rb : fix topic log format and output * ndk_manager.rb, ndk_config.rb : add Client_server_host * 0.4.0 released Fri, 23 Apr 2004 10:35:04 +0900 Koichi Sasada * ndk_manger.rb, ndk_client.rb, ndk_config.rb : support away action with Away_Message, Away_Nick * nadoka.rb : fix version string * ndk_config.rb : ACL -> ACL_Object * 0.3.5 released Fri, 23 Apr 2004 02:45:13 +0900 Koichi Sasada * add a copyright statement to each file * fix a ACL bug Thu, 22 Apr 2004 18:31:39 +0900 Koichi Sasada * rice/irc.rb : mitigate IRC message check(CRLF) Thu, 22 Apr 2004 17:58:25 +0900 Koichi Sasada * ndk_manager.rb : fix on_timer event call * 0.3.4 released Thu, 22 Apr 2004 17:01:45 +0900 Koichi Sasada * ndk_client.rb : fix that send message to a client which has not logged in. * ndk_config.rb : fix ${channel_name} sanitizing * ndk_client.rb (login) : fix client login sequence * ndk_config.rb : fix expand dir with log_dir * ndk_manager.rb : close when client socket when client closed * ndk_config.rb : add Client_server_acl * ndk_config.rb : GC.start when reload config * 0.3.3 released Thu, 22 Apr 2004 11:02:00 +0900 Koichi Sasada * ndk_bot.rb : add on_client_login, on_client_logout event * plugins/sixamo.nb : ver. up sixamo bot san * 0.3.2 released Wed, 21 Apr 2004 21:19:27 +0900 Koichi Sasada * ndk_manager.rb (logging) : add some logging action * ndk_manager.rb : fix a ctcp VERSION bug. Wed, 21 Apr 2004 20:00:59 +0900 Koichi Sasada * ndk_config.rb : change the Server information setting format * ndk_err.rb : add Nadoka::NDK_RestartProgram(not used) * 0.3.0 released Wed, 21 Apr 2004 18:32:21 +0900 Koichi Sasada * plugins/sixamo.nb : modify to use configuration * ndk_config.rb : Hostname use Socket.gethostname Wed, 21 Apr 2004 13:48:42 +0900 Koichi Sasada * plugins/sixamo.nb : added Wed, 21 Apr 2004 02:32:26 +0900 Koichi Sasada * ndk_manager.rb, ndk_client.rb : move 'send_from_client' method to ndk_client * ndk_err.rb : add NDK_QuitProgram class Tue, 20 Apr 2004 18:06:47 +0900 Koichi Sasada * ndk_manager.rb (send_to_bot) : fix bot round robin routine * ndk_bot.rb : add NDK_BotBreak to interrupt bot round robin * ndk_config.rb : $C encode with URI.encode * ndk_config.rb : Bots -> BotFiles * ndk_config.rb : add Log_TimeFormat * ndk_config.rb : Plugin_dir suppoort Enumerable Tue, 20 Apr 2004 13:33:19 +0900 Koichi Sasada * 0.1 released nadoka-0.7.6/plugins/0000755000000000000000000000000011640634705013146 5ustar rootrootnadoka-0.7.6/plugins/checkbot.nb0000644000000000000000000000220510566613051015245 0ustar rootroot# -*-ruby-*- # # Copyright (c) 2004-2005 SASADA Koichi # # 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: checkbot.nb 181 2007-02-20 15:39:21Z znz $ class CheckBot < Nadoka::NDK_Bot def bot_initialize @ch = @bot_config[:ch] || '#nadoka' @tm = @bot_config[:tm] || 30 # min @prevtm = Time.now # override me @botheader = 'checkbot' end def on_timer tm if time? && need_check? check end end def time? tm = Time.now if tm.to_i - @tm * 60 > @prevtm.to_i @prevtm = tm true else false end end ############################## # use this def check_notice text if @ch.respond_to? :each @ch.each{|ch| send_notice(ch, "#{@botheader}: #{text}") sleep 5 # Flood Protection } else send_notice(@ch, "#{@botheader}: #{text}") sleep 5 # Flood Protection end end ############################ # override me def need_check? true end def check # some work end end nadoka-0.7.6/plugins/mailcheckbot.nb0000644000000000000000000001131610403753065016113 0ustar rootroot# -*-ruby-*- # # Copyright (C) 2004 Kazuhiro NISHIYAMA # All rights reserved. # This is free software with ABSOLUTELY NO WARRANTY. # # You can redistribute it and/or modify it under the terms of # the Ruby's licence. # =begin BotConfig = [ { :name => :MailCheckBot, :Maildir => "~/Maildir/.ml.debian-security-announce", :template => "biff(DSA): %{subject}s", :channels => ['#nadoka'], }, { :name => :MailCheckBot, :mh_dir => [ "~/Mail/ml/nadoka", "~/Mail/ml/yarv-dev", ], :channels => %w[#nadoka #nadoka_check], :template => "biff(%{x-ml-name}s): %{subject}s - http://www.atdot.net/mla/%{x-ml-name}s/%{x-mail-count}d", } ] =end require 'nkf' class MailCheckBot < Nadoka::NDK_Bot class Error < StandardError; end class MailCheckInfo def initialize @biffed = Hash.new update_entries do |fields| true end end def entries files = [] @glob_patterns.each do |pattern| files.concat(Dir.glob(pattern)) end files end def mailread(filename) fields = Hash.new("".freeze) header_lines = "" message_id = nil File.foreach(filename) do |line| case line when /^\s*$/ break when /^message-id:\s*/i message_id = $' return message_id, fields if @biffed.key?(message_id) end header_lines.concat(line) end header_lines.split(/^(?!\s)/).each do |line| line = NKF.nkf("-e", line) line.gsub!(/\s+/, ' ') key, value = line.split(/:/, 2) key.downcase! value = value.to_s value.strip! # use last field if same keys found fields[key.freeze] = value.freeze end return message_id, fields rescue # ignore missing files after glob end def update_entries old_entries = @biffed.keys new_entries = entries (new_entries - old_entries).each do |filename| mid, fields = mailread(filename) next unless mid # the file is not probably a mail message next if @biffed.key?(mid) # already biffed yield(fields) @biffed[mid.freeze] = true end if not(old_entries.empty?) and new_entries.empty? @biffed.clear end end end class MaildirInfo < MailCheckInfo def initialize(dir) full_path = File.expand_path(dir).freeze @glob_patterns = [ File.expand_path("cur/*,", dir).freeze, File.expand_path("new/*", dir).freeze, ].freeze super() end end class MhInfo < MailCheckInfo def initialize(dir) full_path = File.expand_path("#{dir}/*") @glob_patterns = [ full_path.freeze, ].freeze super() end end def bot_initialize @on_timer = nil p [:MailCheckBot, :bot_initialize] if $DEBUG @m_infos = {} @bot_config.freeze @channels = @bot_config[:channels].collect do |ch| NKF.nkf("-j -m0", ch).freeze end.freeze @template = (@bot_config[:template] || "biff: %{subject}s").freeze if @bot_config.key?(:Maildir) if @bot_config.key?(:mh_dir) raise Error, "both :Maildir and :mh_dir found in #{@bot_config.inspect}" end @bot_config[:Maildir].each do |dir| raise Error, "duplicated Maildir: #{dir}" if @m_infos.key?(dir) @m_infos[dir] = MaildirInfo.new(dir) end elsif @bot_config.key?(:mh_dir) @bot_config[:mh_dir].each do |dir| raise Error, "duplicated MH dir: #{dir}" if @m_infos.key?(dir) @m_infos[dir] = MhInfo.new(dir) end else raise Error, ":Maildir or :mh_dir not found in #{@bot_config.inspect}" end end def bot_state keys = @m_infos.keys dir, = File.basename(keys[0]) if keys.size > 1 dir = "#{dir}, ..." end "\#<#{self.class}: #{dir} -> #{@channels.join(' ')}>" end def on_timer t p [:MailCheckBot, :on_timer, t] if $DEBUG if @on_timer $stderr.puts "MailCheckBot#on_timer duplicated (t=#{t} and old t=#{@on_timer})" if $DEBUG return end @on_timer = t mailcheck ensure @on_timer = false end def mailcheck @m_infos.each do |dir, m_info| m_info.update_entries do |fields| bark(apply_template(fields)) end end end def apply_template(fields) msg = @template.dup msg.gsub!(/%\{([a-z0-9_\-]+)\}([sd])/i) do field_name, field_type = $1, $2 field_name.downcase! case field_type when 's' fields[field_name].to_s when 'd' fields[field_name].to_i.to_s else "(field type bug: `#{field_type}')" end end NKF.nkf("-j -m0", msg).freeze end def bark(msg) @channels.each do |ch| send_notice(ch, msg) end end end nadoka-0.7.6/plugins/twitterbot.nb0000755000000000000000000000676611441124642015711 0ustar rootroot# -*-ruby-*- # nadoka-twit # # = Usage # # == Get consumer key # # 1. access https://twitter.com/apps/new # 2. register it # 3. memo 'Consumer key' and 'Consumer secret' # # == Get access token # # 1. run this script with consumer key and consumer secret like: # ruby twitterbot.nb # 2. memo access_token and access_token_secret # # == Setting nadokarc # # 1. set :consumer_key, :consumer_secret, :access_token, # and :acccess_token_secret # # = Configuration # # == :ch # # target channel # # == :pattern # # pattern for messages to send twitter # # == :nkf_encoding # # the encoding of messages # # == :consumer_key, :consumer_secret # # Consumer key and consumer secret # # == :access_token, :acccess_token_secret # # Access token and access token secret # require 'time' require 'rubygems' require 'rubytter' if __FILE__ == $0 key = ARGV.shift secret = ARGV.shift unless key && secret puts "Usage: #$0 " end oauth = Rubytter::OAuth.new(key, secret) request_token = oauth.get_request_token system('open', request_token.authorize_url) || puts("Access here: #{request_token.authorize_url}\nand...") print "Enter PIN: " pin = gets.strip access_token = request_token.get_access_token( :oauth_token => request_token.token, :oauth_verifier => pin ) puts ":access_token => '#{access_token.token}'," puts ":access_token_secret => '#{access_token.secret}'," exit end class TwitterBot < Nadoka::NDK_Bot def bot_initialize @ch = @bot_config.fetch(:ch, nil) @pattern = @bot_config.fetch(:pattern, />tw$/) @nkf_encoding = @bot_config.fetch(:nkf_encoding, nil) consumer = OAuth::Consumer.new( @bot_config.fetch(:consumer_key, nil), @bot_config.fetch(:consumer_secret, nil), :site => 'http://twitter.com') access_token = OAuth::AccessToken.new(consumer, @bot_config.fetch(:access_token, nil), @bot_config.fetch(:access_token_secret, nil)) @rt = OAuthRubytter.new(access_token) @current_id = -1 end def on_timer(t) @rt.friends_timeline.each do |status| id = status.id.to_i next unless @current_id < id @current_id = id time = Time.parse(status.created_at) next if time + 5 * 60 < Time.now text = status.text.tr("\r\n", ' ') text = NKF.nkf('--ic=UTF-8 --oc=' + @nkf_encoding, text) if @nkf_encoding send_notice @ch, "#{time.strftime('%H:%M')} #{status.user.screen_name}: #{text}" end rescue Exception => err puts_error_message(err) end def on_client_privmsg(client, ch, message) return unless @ch.nil? or @ch.upcase == ch.upcase unless @pattern =~ message slog 'pattern unmatch, ignored' return end text = message.sub(@pattern, '') text = NKF.nkf('--oc=UTF-8 --ic=' + @nkf_encoding, text) if @nkf_encoding slog((@rt.update(text) ? 'sent to twitter: ' : 'twitter send faild: ') + message) rescue Exception => err puts_error_message(err) end def slog(msg, nostamp = false) current_method = caller.first[/:in \`(.*?)\'/, 1].to_s msg.each do |line| @logger.slog "#{self.class.to_s}##{current_method} #{line}", nostamp end end private def puts_error_message(err) if err.is_a?(Rubytter::APIError) @logger.slog "%s: %s (%s) %s" % [err.backtrace[0], err.message, err.class, err.response] else @logger.slog "%s: %s (%s)" % [err.backtrace[0], err.message, err.class] end @logger.slog err.backtrace.select{|l|/\A\/home/=~l} end end nadoka-0.7.6/plugins/drbot.nb0000644000000000000000000000322110566613051014574 0ustar rootroot# -*-ruby-*- # # Copyright (c) 2004-2005 SASADA Koichi # # 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: drbot.nb 181 2007-02-20 15:39:21Z znz $ # =begin == Abstract An example using DRb. You can access to nadoka via DRb protocol. See sample drbcl.rb == Configuration BotConfig = [ { :name => :DRbot, :port => 12345, :host => '', } ] =end require 'drb/drb' require 'observer' class DRbot < Nadoka::NDK_Bot class Dispatcher include Observable def initialize bot @bot = bot end def send_to_client prefix, command, args changed notify_observers(prefix, command, args) end def recv_from_client *args @bot.kick_from_client args end def notify_observers(*arg) if defined? @observer_state and @observer_state if defined? @observer_peers @observer_peers.dup.each{|e| begin e.update(*arg) rescue Exception @observer_peers.delete e end } end @observer_state = false end end end def bot_initialize @port = @bot_config.fetch(:port, 12346) @host = @bot_config.fetch(:host, '') @invoker = Dispatcher.new(self) @uri = "druby://#{@host}:#{@port}" @drbserver = DRb.start_service(@uri, @invoker) end def bot_destruct @drbserver.stop_service end def on_every_message prefix, command, *args @invoker.send_to_client prefix, command, args end def recv_from_client *args end end nadoka-0.7.6/plugins/translatebot.nb0000644000000000000000000001674311640616724016205 0ustar rootroot# -*-ruby-*- # # Copyright (c) 2010 SASADA Koichi # # 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. # =begin == Usage with irc client trans> hello -> translate hello as a English to Japanese. trans:ja> hello -> ditto. trans:en,ja> hello -> ditto. trans:(([lang_from],)[lang_to])> [phrase] -> translate [phrase] as lang_from to lang_to. transj> [phrase] -> translate to Japanese transe> [phrase] -> translate to English == Configuration: BotConfig = [ { :name => :TranslateBot, :ch => /.*/, :referer => 'http://rubyforge.org/projects/nadoka/', # Register URL at http://code.google.com/intl/ja/apis/ajaxsearch/signup.html # and set your URL to :referer and your API key to :api_key if you want. :to_lang => 'ja', :to_lang2 => 'en', :ch_kcode => :tojis, }, ] =end require 'iconv' require 'kconv' require 'shellwords' require 'cgi' require 'open-uri' begin require 'json' rescue LoadError require 'rubygems' require 'json' end class TranslateBot < Nadoka::NDK_Bot def bot_initialize @available_channel = @bot_config[:ch] || /.*/ @search_default_lang = (@bot_config[:search_default_lang] || 'ja').sub(/^lang_/, '') @referer = @bot_config[:referer] || 'http://rubyforge.org/projects/nadoka/' @ch_kcode = @bot_config.fetch(:ch_kcode, :tojis) @to_lang = @bot_config.fetch(:to_lang, 'ja') @to_lang2 = @bot_config.fetch(:to_lang2, 'en') @bing_appid = @bot_config.fetch(:bing_appid, nil) end def on_privmsg prefix, ch, msg if @available_channel === ch and /^tr/ =~ msg if response = dispatch_command(msg) response.each{|r| send_notice(ch, r) if r } end end end SHORT_LANG = {'e' => 'en', 'j' => 'ja'} def dispatch_command msg begin case msg when /^trans(?:late)?(:(.*?))?>\s*(.+)/o translate($3, *parse_trans_option($2, $3)) when /^trans([ej])>\s*(.+)/o translate($2, nil, SHORT_LANG[$1]) when /^trans(?:late)?r(:(.*?))?>\s*(.+)/o translate_r($3, *parse_trans_option($2, $3)) when /^translang>(.+)/ lang = $1.strip desc = 'Unknown. See http://code.google.com/intl/ja/apis/ajaxlanguage/documentation/reference.html#LangNameArray' r = LANGUAGE_MAP_S2L.fetch(lang.downcase, LANGUAGE_MAP.fetch(lang.upcase, desc)) "translang> #{lang} = #{r}" end rescue Exception => e "translate bot: #{e.message}" end end def detect_lang str uri = "http://ajax.googleapis.com/ajax/services/language/detect?v=1.0&q=" uri << CGI.escape(str.toutf8) result = open(uri, "Referer" => @referer) do |f| JSON.parse(f.read) end if result["responseData"] result["responseData"]["language"] end end def parse_trans_option opt, str case opt when nil from_lang = detect_lang(str) to_lang = @to_lang if to_lang == from_lang to_lang = @to_lang2 end [from_lang, to_lang] when /\A([\w\-]+)[, \>]([\w\-]+)\z/ [$1, $2] when /\A([\w\-]+)\z/ [nil, $1] else raise "can't parse translation option: #{opt}" end end def translate phrase, from_lang, to_lang r = [] gr = translate_ggl(phrase, from_lang, to_lang) r << "g:translate bot (#{gr[1]}): #{gr[0]}" if @bing_appid mr = translate_ms(phrase, from_lang, to_lang) if @bing_appid r << "m:translate bot (#{mr[1]}): #{mr[0]}" end r end def translate_r phrase, from_lang, to_lang rs = [] %w(ggl ms).each{|system| r = send("translate_#{system}", phrase, from_lang, to_lang) from_lang = r[2] first = r[0] if from_lang r = send("translate_#{system}", r[0], to_lang, from_lang) rs << "#{system.split(//)[0]}:trans_r (#{from_lang}>#{to_lang}>#{from_lang}): #{r[0]} (#{first})" end } rs end def translate_ggl(phrase, from_lang, to_lang) uri = 'http://ajax.googleapis.com/ajax/services/language/translate?v=1.0&q=' uri << CGI.escape(phrase.toutf8) uri << "&langpair=#{from_lang}%7C#{to_lang}" result = open(uri, "Referer" => @referer) do |f| JSON.parse(f.read) end if result["responseData"] text = CGI.unescapeHTML(result["responseData"]["translatedText"]) text = text.send(@ch_kcode) if @ch_kcode from_lang ||= result["responseData"]["detectedSourceLanguage"] opts = "#{from_lang}>#{to_lang}" [text, opts, from_lang] else opts = "#{from_lang ? "from #{from_lang} to " : ''}#{to_lang}" ["#{result["responseDetails"]} (#{uri})", opts] end end ## ms translate def get_result_ms result # puts result doc = REXML::Document.new(result) doc.elements.map{|e| e.text}[0] end def translate_ms phrase, from_lang, to_lang api_url = 'http://api.microsofttranslator.com/V2/Http.svc/Translate' uri = "#{api_url}?appId=#{@bing_appid}&text=#{CGI.escape(phrase.toutf8)}&to=#{CGI.escape(to_lang)}" begin text = get_result_ms open(uri, "Referer" => @referer).read text = text.send(@ch_kcode) if @ch_kcode opts = "#{from_lang}>#{to_lang}" [text, opts, from_lang] rescue OpenURI::HTTPError => e opts = "#{from_lang ? "from #{from_lang} to " : ''}#{to_lang}" ["#{e.message} (uri)", opts] end end # copy from http://code.google.com/intl/ja/apis/ajaxlanguage/documentation/reference.html LANGUAGE_MAP = { 'AFRIKAANS' => 'af', 'ALBANIAN' => 'sq', 'AMHARIC' => 'am', 'ARABIC' => 'ar', 'ARMENIAN' => 'hy', 'AZERBAIJANI' => 'az', 'BASQUE' => 'eu', 'BELARUSIAN' => 'be', 'BENGALI' => 'bn', 'BIHARI' => 'bh', 'BULGARIAN' => 'bg', 'BURMESE' => 'my', 'CATALAN' => 'ca', 'CHEROKEE' => 'chr', 'CHINESE' => 'zh', 'CHINESE_SIMPLIFIED' => 'zh-CN', 'CHINESE_TRADITIONAL' => 'zh-TW', 'CROATIAN' => 'hr', 'CZECH' => 'cs', 'DANISH' => 'da', 'DHIVEHI' => 'dv', 'DUTCH'=> 'nl', 'ENGLISH' => 'en', 'ESPERANTO' => 'eo', 'ESTONIAN' => 'et', 'FILIPINO' => 'tl', 'FINNISH' => 'fi', 'FRENCH' => 'fr', 'GALICIAN' => 'gl', 'GEORGIAN' => 'ka', 'GERMAN' => 'de', 'GREEK' => 'el', 'GUARANI' => 'gn', 'GUJARATI' => 'gu', 'HEBREW' => 'iw', 'HINDI' => 'hi', 'HUNGARIAN' => 'hu', 'ICELANDIC' => 'is', 'INDONESIAN' => 'id', 'INUKTITUT' => 'iu', 'ITALIAN' => 'it', 'JAPANESE' => 'ja', 'KANNADA' => 'kn', 'KAZAKH' => 'kk', 'KHMER' => 'km', 'KOREAN' => 'ko', 'KURDISH'=> 'ku', 'KYRGYZ'=> 'ky', 'LAOTHIAN'=> 'lo', 'LATVIAN' => 'lv', 'LITHUANIAN' => 'lt', 'MACEDONIAN' => 'mk', 'MALAY' => 'ms', 'MALAYALAM' => 'ml', 'MALTESE' => 'mt', 'MARATHI' => 'mr', 'MONGOLIAN' => 'mn', 'NEPALI' => 'ne', 'NORWEGIAN' => 'no', 'ORIYA' => 'or', 'PASHTO' => 'ps', 'PERSIAN' => 'fa', 'POLISH' => 'pl', 'PORTUGUESE' => 'pt-PT', 'PUNJABI' => 'pa', 'ROMANIAN' => 'ro', 'RUSSIAN' => 'ru', 'SANSKRIT' => 'sa', 'SERBIAN' => 'sr', 'SINDHI' => 'sd', 'SINHALESE' => 'si', 'SLOVAK' => 'sk', 'SLOVENIAN' => 'sl', 'SPANISH' => 'es', 'SWAHILI' => 'sw', 'SWEDISH' => 'sv', 'TAJIK' => 'tg', 'TAMIL' => 'ta', 'TAGALOG' => 'tl', 'TELUGU' => 'te', 'THAI' => 'th', 'TIBETAN' => 'bo', 'TURKISH' => 'tr', 'UKRAINIAN' => 'uk', 'URDU' => 'ur', 'UZBEK' => 'uz', 'UIGHUR' => 'ug', 'VIETNAMESE' => 'vi', 'UNKNOWN' => '' } LANGUAGE_MAP_S2L = LANGUAGE_MAP.invert end nadoka-0.7.6/plugins/pastebot.nb0000644000000000000000000000177511450645335015322 0ustar rootroot=begin This plugin is test version. =end require 'open-uri' class PasteBot < Nadoka::NDK_Bot def bot_initialize @ch = @bot_config[:ch] || /./ @msg = @bot_config[:mgs] || /\Apaste>/ @service_uri = @bot_config[:service_uri] || 'http://www.atdot.net/sp' @fmsg = @bot_config[:mgs] || /\Afpaste>/ @fservice_uri = @bot_config[:service_uri] || 'http://www.atdot.net/fp' end def nick_escape nick nick.gsub(/[^A-Za-z\d\-_]/, '_') end def on_privmsg prefix, ch, msg nick = nick_escape prefix.nick if @ch === ch if @msg === msg nid = '' open("#{@service_uri}/check/newid"){|f| nid = f.gets } send_notice ch, "#{@service_uri}/view/#{nid}_#{nick}" end if @fmsg === msg nid = '' open("#{@fservice_uri}/check/newid"){|f| nid = f.gets } send_notice ch, "#{@fservice_uri}/view/#{nid}_#{nick}" end end end end nadoka-0.7.6/plugins/autoawaybot.nb0000644000000000000000000000222211450645335016024 0ustar rootroot# -*-ruby-*- # # Copyright (c) 2004-2005 SASADA Koichi # # 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. # # # This bot is created by # akira yamada # # $Id$ # =begin == Abstract Auto away if no action == Configuration BotConfig = [ { :name => :AutoAwayBot, :threshold => 15*60, # sec :message => 'Away', } ] =end class AutoAwayBot < Nadoka::NDK_Bot def bot_initialize @threshold = @bot_config.fetch(:threshold, 15*60).to_i @message = @bot_config.fetch(:message, 'Away') @lastseen = Time.now @in_away = false end def on_client_privmsg client, ch, msg if @threshold > 0 @lastseen = Time.now end if @in_away @manager.send_to_server Nadoka::Cmd.away() end end def on_timer time if @threshold > 0 && Time.now - @lastseen > @threshold && !@in_away @manager.send_to_server Nadoka::Cmd.away(@message) end end def on_rpl_unaway *arg @in_away = false end def on_rpl_nowaway *arg @in_away = true end end nadoka-0.7.6/plugins/modemanager.nb0000644000000000000000000000611411450645335015750 0ustar rootroot# -*-ruby-*- # # Copyright (c) 2004-2005 SASADA Koichi # # 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: modemanager.nb 240 2010-09-29 14:19:09Z znz $ # =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 [ or 'all'] <+-> pattern <+-> /regexp pattern/ : '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 nadoka-0.7.6/plugins/cronbot.nb0000644000000000000000000000064010566613051015132 0ustar rootroot# -*-ruby-*- # # Copyright (c) 2004-2005 SASADA Koichi # # 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: cronbot.nb 181 2007-02-20 15:39:21Z znz $ # class CronBot < Nadoka::NDK_Bot def on_timer t @state.channels.each{|ch| send_notice(ch, t.to_s) } end end nadoka-0.7.6/plugins/dictbot.nb0000644000000000000000000000164111450645335015121 0ustar rootroot=begin This plugin is test version. =end require 'uri' require 'open-uri' require 'kconv' class DictBot < Nadoka::NDK_Bot def bot_initialize @available_channel = @bot_config[:ch] || /.*/ end def on_privmsg prefix, ch, msg if @available_channel === ch if /\Adic(.)>\s*(.+)\s*/ =~ msg res = yahoo_dict $1, $2.toeuc send_notice(ch, res) end end end YAHOO_DICT_TYPE ={ 't' => 2, 'e' => 1, 'j' => 0, 'w' => 3, 'r' => 5, } def yahoo_dict type, word "dict bot> " + if type = YAHOO_DICT_TYPE[type] word = URI.encode(word) uri = "http://dic.yahoo.co.jp/dsearch?p=#{word}&stype=0&dtype=#{type}" open(uri){|f| if // =~ f.read "#{$1.tosjis.tojis} - #{uri}" else uri end } else "unknown type: #{type}" end end end nadoka-0.7.6/plugins/autodumpbot.nb0000644000000000000000000001225111450645335016033 0ustar rootroot# -*-ruby-*- # # Copyright (c) 2004-2005 SASADA Koichi # # 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. # # # This bot is created by # akira yamada # # $Id$ # =begin == Abstract Auto save configuration == Configuration add these program to nadokarc(or your configuration file) begin dump_file = File.join(Log_dir, Setting_name + '-channel_info.dump') channel_info = {} begin File.open(dump_file, 'r') {|i| channel_info = Marshal.load(i)} rescue TypeError File.open(dump_file, 'r') {|i| channel_info = YAML.load(i.read)} end Channel_info = channel_info rescue Channel_info = { } end more detail, see [nadoka:162] =end class AutoDumpBot < Nadoka::NDK_Bot def bot_initialize @auto_dump = @bot_config.fetch(:auto_dump, true) @default_dump_style = @bot_config.fetch(:dump_style, 'yaml') @default_timing = @bot_config.fetch(:dump_timing, :startup) @cinfo = {} @manager.state.channels.each do |ch| @manager.send_to_server Nadoka::Cmd.mode(ch) end end def canonical_channel_name ch @manager.state.canonical_channel_name ch end def on_join prefix, ch if @manager.state.current_nick == prefix.nick dch = canonical_channel_name(ch) @cinfo[dch] ||= {} @cinfo[dch][:name] = ch @cinfo[dch][:mode] ||= [] @manager.send_to_server Nadoka::Cmd.mode(ch) end end def on_part prefix, ch, msg='' if @manager.client_count > 0 && @manager.state.current_nick == prefix.nick @cinfo.delete(canonical_channel_name(ch)) end end def on_client_logout count, client if count == 1 && @auto_dump dump_cinfo end end MODE_WITH_NICK_ARG = 'ov' MODE_WITH_ARGS = 'klbeI' MODE_WITHOUT_ARGS = 'aimnqpsrt' def on_mode prefix, nick, ch, *args dch = canonical_channel_name(ch) if @manager.state.current_channels.has_key? dch @cinfo[dch] ||= {} @cinfo[dch][:name] = ch @cinfo[dch][:mode] ||= [] while mode = args.shift modes = mode.split(//) flag = modes.shift modes.each{|m| if MODE_WITH_NICK_ARG.include? m #chg_cinfo dch, flag, m, args.shift elsif MODE_WITH_ARGS.include? m chg_cinfo dch, flag, m, args.shift elsif MODE_WITHOUT_ARGS.include? m chg_cinfo dch, flag, m, nil end } end end end def on_rpl_channelmodeis prefix, nick, ch, *arg on_mode(prefix, nick, ch, *arg) end def on_nadoka_command client, command, *params if command == 'dump' dump_cinfo(params.shift) raise ::Nadoka::NDK_BotSendCancel end end def chg_cinfo ch, flag, mode, arg @cinfo[ch][:mode] ||= [] @cinfo[ch][:mode].delete_if do |m| m[0, 1] == mode end if flag == '+' if arg @cinfo[ch][:mode] << mode + ' ' + arg else @cinfo[ch][:mode] << mode end end end def dump_cinfo(style = nil) style = @default_dump_style unless style channel_info = {} @cinfo.keys.each do |ch| name = @cinfo[ch][:name] if @state.current_channels.include?(ch) name = @state.current_channels[ch].name end channel_info[name] = cinfo = {} config = @manager.instance_eval {@config} if config.default_channels.detect {|tch| canonical_channel_name(tch) == ch} cinfo[:timing] = :startup elsif config.login_channels.detect {|tch| canonical_channel_name(tch) == ch} cinfo[:timing] = :login else cinfo[:timing] = @default_timing end if @cinfo[ch][:mode].include?('i') cinfo[:timing] = :startup elsif !cinfo.include?(:timing) cinfo[:timing] = :login end @cinfo[ch][:mode].each do |m| if m[0] == ?k cinfo[:key] = m[2 .. -1] end end m1, m2 = @cinfo[ch][:mode].partition {|m| m.size == 1} imode = nil unless m1.empty? imode ||= '+' imode << m1.join('') end unless m2.empty? imode ||= '+' imode << m2.join(' +') end cinfo[:initial_mode] = imode end begin dump_file = File.join(@config.log_dir, @config.setting_name + '-channel_info.dump') File.open(dump_file, 'wb') do |f| f.chmod(0600) dump_channel_info(channel_info, style, f) end @logger.slog "current channel information was dumped (#{style})" rescue Exception => e @logger.slog "current channel information could not be dumped" @logger.slog e.message end end def dump_channel_info(channel_info, style, port) if style == 'marshal' Marshal.dump(channel_info, port) elsif style == 'yaml' require 'yaml' YAML.dump(channel_info, port) elsif style == 'text' port.puts "Channel_info = {" channel_info.each do |ch, info| port.puts " #{ch.dump} => {" info.each do |tag, value| if tag.kind_of?(Symbol) if value.kind_of?(Symbol) port.puts " :#{tag.to_s} => :#{value.to_s}," else port.puts " :#{tag.to_s} => #{value.to_s.dump}," end end end port.puts " }," end port.puts "}" else raise RuntimeError, "unsupported dump style: #{style.dump}" end end end # vim:filetype=ruby: nadoka-0.7.6/plugins/sendpingbot.nb0000644000000000000000000000052310566613051016000 0ustar rootroot# -*-ruby-*- # # Copyright (c) 2004-2006 SASADA Koichi # # 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. # # # class SendPingBot < Nadoka::NDK_Bot def on_timer *args @manager.ping_to_clients end end nadoka-0.7.6/plugins/xibot.nb0000644000000000000000000000267011450645335014621 0ustar rootroot# # Xi Bot # # No rights reserved. # # Synopsis: # xi> 2d10 (two dice of ten) # [2d10] 13 = 7 + 6 # xi> 5d # [5d6] 14 = 3 + 1 + 3 + 1 + 6 # xi>100 # [1d100] 26 # class XiBot < Nadoka::NDK_Bot def bot_initialize @available_channel = @bot_config[:ch] || /.*/ end def on_privmsg prefix, ch, msg return unless @available_channel === ch return unless /\Axi\s*>\s*/ =~ msg case $~.post_match when /character/ %w/STR DEX CON INT WIS CHA/.each do |name| values = (1..3).map{|i|rand(6)+1} sum = values.inject(0){|s, i|s += i} send_notice(ch, '%s: %2d = %s' % [name, sum, values.join(' + ')]) end when /char/ values = %w/STR DEX CON INT WIS CHA/.map do |name| '%s: %2d' % [name, (1..4).map{|i|rand(6)+1}.sort.last(3).inject(0){|s, i|s += i}] end send_notice(ch, "#{prefix.nick}: #{values.join(', ')}") when /(?:(\d+)d)?(\d+)?(?:\*([1-9]))?/ count = $1.to_i count = 1 unless (1..100).include? count max = $2.to_i max = 6 unless (1..1_000_000_000).include? max ($3 ? $3.to_i : 1).times do values = (1..count).map{|i|rand(max)+1} sum = values.inject(0){|s, i|s += i} if count == 1 send_notice(ch, '%s: [%dd%d] %d' % [prefix.nick,count, max, sum]) else send_notice(ch, '%s: [%dd%d] %d = %s' % [prefix.nick,count, max, sum, values.join(' + ')]) end end end end end nadoka-0.7.6/plugins/drbcl.rb0000755000000000000000000000071110073314176014557 0ustar rootroot# # An example for drbot.nb # # require 'drb/drb' class Invokee include DRbUndumped def initialize invoker @invoker = invoker @invoker.add_observer self end def update *args p args end def destruct @invoker.delete_observer self end end uri = ARGV.shift || raise DRb.start_service invoker = DRbObject.new(nil, uri) begin invokee = Invokee.new(invoker) p invoker puts '---' gets ensure invokee.destruct end nadoka-0.7.6/plugins/timestampbot.nb0000644000000000000000000000241311450645335016177 0ustar rootroot# -*-ruby-*- # # Copyright (c) 2004-2005 SASADA Koichi # # 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: timestampbot.nb 240 2010-09-29 14:19:09Z znz $ # =begin == Abstract Add time stamp to each log. == Configuration BotConfig = [ { :name => :TimeStampBot, :interval => 60 * 60, # sec :stampformat => '== %y/%m/%d-%H:%M:%S ==========================================', :client => false, } ] =end class TimeStampBot < Nadoka::NDK_Bot def bot_initialize @interval = @bot_config.fetch(:interval, 60 * 60) # default: 1 hour @stampformat = @bot_config.fetch(:stampformat, '== %y/%m/%d-%H:%M:%S ==========================================') @client = @bot_config.fetch(:client, false) @nexttime = nexttime end def nexttime t = (Time.now.to_i + @interval) Time.at(t - (t % @interval)) end def on_timer tm if tm >= @nexttime stamp_log @nexttime = nexttime end end def stamp_log msg = @nexttime.strftime(@stampformat) @state.channels.each{|ch| @logger.clog(ch, msg, true) } @logger.slog(msg, true) if @client end end nadoka-0.7.6/plugins/opshop.nb0000644000000000000000000000071210566613051014774 0ustar rootroot# -*-ruby-*- # # Copyright (c) 2004-2005 SASADA Koichi # # 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. # # # operator shop # # $Id: opshop.nb 181 2007-02-20 15:39:21Z znz $ # class OpShop < Nadoka::NDK_Bot def on_join prefix, ch if prefix.nick != @state.nick change_mode(ch, "+o", prefix.nick) end end end nadoka-0.7.6/plugins/samplebot.nb0000644000000000000000000000102210566613051015445 0ustar rootroot# -*-ruby-*- # # Copyright (c) 2004-2005 SASADA Koichi # # 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: samplebot.nb 181 2007-02-20 15:39:21Z znz $ # # bot_file_name and BotClassName must be same name # (BotClassName.downcase == bot_file_name) class SampleBot < Nadoka::NDK_Bot # Yes person def on_privmsg prefix, ch, msg send_notice(ch, "Yes, #{prefix.nick}!") end end nadoka-0.7.6/plugins/tenkibot.nb0000644000000000000000000000511411501536004015274 0ustar rootroot# -*-ruby-*- # # Copyright (c) 2004-2006 SASADA Koichi # # 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. # =begin == Abstract Answer weather information using "Livedoor Weather Web Service / LWWS". LWWS: http://weather.livedoor.com/weather_hacks/webservice.html == Usage tenki> [CITY] tenki:[today|tomorrow|dayaftertomorrow]> [CITY] [CITY] should be city name in Kanji listed on following table. http://weather.livedoor.com/forecast/rss/forecastmap.xml If timing is not specified, show today's information. == Configuration BotConfig = [ { :name => :TenkiBot, :ch => /nadoka/, # default: /.*/ } ] =end require 'open-uri' require 'pp' require 'kconv' require 'rexml/document' require 'date' class TenkiBot < Nadoka::NDK_Bot CityIDs = {} def bot_initialize bot_init_utils open('http://weather.livedoor.com/forecast/rss/forecastmap.xml'){|f| f.each{|line| if /city title="(.+?)" id="(\d+)"/ =~ line CityIDs[$1.toeuc] = $2.to_i end } } end def tenki city, timing doc = open( "http://weather.livedoor.com/forecast/webservice/rest/v1?" \ "city=#{CityIDs.fetch(city)}&day=#{timing}"){|f| REXML::Document.new f.read } title = doc.elements['/lwws/title/'].text.toeuc telop = doc.elements['/lwws/telop/'].text.toeuc link = doc.elements['/lwws/link/'].text.toeuc desc = doc.elements['/lwws/description/'].text.toeuc max = doc.elements['/lwws/temperature/max/celsius/'].text min = doc.elements['/lwws/temperature/min/celsius/'].text date = Date.parse(doc.elements['/lwws/forecastdate/'].text) datestr = date.strftime('%m/%d') desc.sub!(/\.\.\..*/m, '...') celsius = [] celsius << "max: #{max}" if max celsius << "min: #{min}" if min unless celsius.empty? celsius = "(#{celsius.join(', ')}) " end "#{title} (#{datestr}): #{telop} - #{celsius}#{desc} - #{link}".tojis end def on_privmsg prefix, ch, msg return unless @available_channel === ch return if same_bot?(ch) if /\Atenki(|:(today|tomorrow|dayaftertomorrow))>(.+)/ =~ msg city = $3.strip.toeuc timing = ($2 || 'today').strip begin result = tenki(city, timing).gsub(/\n/, ' ') rescue IndexError result = "Unknown city. Check city title on http://weather.livedoor.com/forecast/rss/forecastmap.xml" rescue => e result = "#{e}" end send_notice ch, "tenki bot: #{result}" end end end nadoka-0.7.6/plugins/opensearchbot.nb0000644000000000000000000001074711634151764016336 0ustar rootroot# -*-ruby-*- # # Copyright (C) 2011 Kazuhiro NISHIYAMA # # 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. # =begin == Usage with irc client bing> keyword -> search keyword by bing googlecode> keyword -> search keyword by Google Code Search Data API (Deprecated) koders> keyword -> search keyword by koders == Configuration: BotConfig << { :name => :OpenSearchBot, :bot_name => 'bing', :ch => //, :referer => 'http://rubyforge.org/projects/nadoka/', :ch_kcode => :jis, :html => 'http://www.bing.com/search?q={searchTerms}', :rss => 'http://api.search.live.com/rss.aspx?source=web&query={searchTerms}' , } BotConfig << { :name => :OpenSearchBot, :bot_name => 'googlecode', :ch => //, :ch_kcode => :jis, :referer => 'http://rubyforge.org/projects/nadoka/', # Google Code Search Data API (Deprecated) # http://code.google.com/intl/ja/apis/codesearch/docs/2.0/developers_guide.h tml :html => 'http://www.google.com/codesearch?q={searchTerms}', :rss => 'http://www.google.com/codesearch/feeds/search?q={searchTerms}', } BotConfig << { :name => :OpenSearchBot, :bot_name => 'koders', :ch => //, :referer => 'http://rubyforge.org/projects/nadoka/', :ch_kcode => :jis, # http://www.koders.com/search/KodersDescriptionOS1_1.xml :html => 'http://www.koders.com/?s={searchTerms}', :rss => 'http://www.koders.com/?s={searchTerms}&results=code&output=rss&OSve rsion=1.1', } =end require 'open-uri' require 'uri' require 'cgi' class OpenSearch def initialize(options) @html = options[:html] @rss = options[:rss] @referer = options[:referer] || 'http://rubyforge.org/projects/nadoka/' end def result(key) escaped_key = CGI.escape(key) link = @html.sub(/\{searchTerms\}/) { escaped_key } uri = @rss.sub(/\{searchTerms\}/) { escaped_key } open(uri, "Referer" => @referer) do |f| result = f.read if /<([A-Za-z]+):totalResults>(\d+)<\/\1:totalResults>/ =~ result total = $2.to_i return "#{total} result#{total > 1 ? 's' : ''} in #{link}" else return "#{key} - not found in #{link}" end end end end if __FILE__ == $0 h = { 'bing' => { :referer => 'http://rubyforge.org/projects/nadoka/', :html => 'http://www.bing.com/search?q={searchTerms}', :rss => 'http://api.search.live.com/rss.aspx?source=web&query={searchTerms}', }, 'googlecode' => { :referer => 'http://rubyforge.org/projects/nadoka/', # Google Code Search Data API (Deprecated) # http://code.google.com/intl/ja/apis/codesearch/docs/2.0/developers_guide.html :html => 'http://www.google.com/codesearch?q={searchTerms}', :rss => 'http://www.google.com/codesearch/feeds/search?q={searchTerms}', }, 'koders' => { :referer => 'http://rubyforge.org/projects/nadoka/', # http://www.koders.com/search/KodersDescriptionOS1_1.xml :html => 'http://www.koders.com/?s={searchTerms}', :rss => 'http://www.koders.com/?s={searchTerms}&results=code&output=rss&OSversion=1.1', }, 'youtube' => { :referer => 'http://rubyforge.org/projects/nadoka/', :html => 'http://www.youtube.com/results?search_query={searchTerms}', :rss => 'http://gdata.youtube.com/feeds/api/videos?q={searchTerms}', }, } engine = ARGV.shift if h.key?(engine) open_search = OpenSearch.new(h[engine]) ARGV.each do |key| result = open_search.result(key) puts result end else STDERR.puts "usage: #{$0} {#{h.keys.sort.join('|')}} key ..." end exit end class OpenSearchBot < Nadoka::NDK_Bot def bot_initialize if @bot_config.key?(:channels) channels = '\A(?:' + @bot_config[:channels].collect{|ch| Regexp.quote(ch) }.join('|') + ')\z' @available_channel = Regexp.compile(channels) else @available_channel = @bot_config[:ch] || // end @bot_name = @bot_config[:bot_name] || 'OpenSearchBot' @open_search = OpenSearch.new(@bot_config) @pattern = @bot_config[:pattern] || /\A#{Regexp.quote(@bot_name)}\s*[<:>]\s*(.+)/ @ch_kcode = @bot_config[:ch_kcode] end def on_privmsg prefix, ch, msg if @pattern =~ msg key = $1 if @ch_kcode == :jis ret = @open_search.result(key.toutf8).tojis else ret = @open_search.result(key) end send_notice ch, "#{@bot_name} bot: #{ret}" end end end nadoka-0.7.6/plugins/identifynickserv.nb0000644000000000000000000000151711204751731017046 0ustar rootroot# -*-ruby-*- # # Copyright (c) 2009 Kazuhiro NISHIYAMA # # 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. # =begin == Abstract send IDENTIFY to NickServ. == Configuration BotConfig << { :name => :IdentifyNickServ, #:nickserv => "NickServ", :nick => "nadoka", :pass => "hoge", } =end class IdentifyNickServ < Nadoka::NDK_Bot def bot_initialize @nickserv = @bot_config.fetch(:nickserv, "NickServ") @nick = @bot_config.fetch(:nick, false) @pass = @bot_config.fetch(:pass, false) end def on_server_connected(*args) if @pass if @nick send_privmsg @nickserv, "IDENTIFY #{@nick} #{@pass}" else send_privmsg @nickserv, "IDENTIFY #{@pass}" end end end end nadoka-0.7.6/plugins/gonzuibot.nb0000644000000000000000000000173610467014666015522 0ustar rootroot# gonzui bot # require 'open-uri' require 'uri' class GonzuiBot < Nadoka::NDK_Bot ResultRegexp = %r(>Results (\d+) - (\d+) of (\d+)\s*(.+)/ =~ msg send_notice ch, "gonzui bot: #{gonzui_result($1, $2)}" end end EngineURI = { 'raa' => 'http://raa.ruby-lang.org/gonzui/', 'gnome' => 'http://gonzui.tagonome.org/', 'cpan' => 'http://cpansearch.bulknews.net/', 'b-src' => 'http://b-src.cbrc.jp/', } def gonzui_result engine, key engine ||= 'raa' key_uri = URI.encode(key) engine_uri = EngineURI[engine.downcase] return "unknown engine: #{engine}" unless engine_uri url = "#{engine_uri}search?q=#{key_uri}" open(url){|f| result = f.read if ResultRegexp =~ result "#{$3} for #{key} - #{url}" else "#{key} - not found in #{engine}" end } end end nadoka-0.7.6/plugins/googlebot.nb0000644000000000000000000002040311622234070015436 0ustar rootroot# -*-ruby-*- # # Copyright (c) 2004-2005 SASADA Koichi # Copyright (c) 2009, 2010 Kazuhiro NISHIYAMA # # 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: googlebot.nb 253 2011-08-15 15:16:08Z znz $ # =begin == Usage with irc client google> keyword -> search keyword by google with default search langage google:[lang]> keyword -> search keyword by google with [lang] langage googlec> k1 k2 k3 k4 k5(max 5 words) -> search and show each hit count googlec> k1 k2 k3 k4 k5(max 5 words) -> search and show each hit count with default count language googlec:[lang]> k1 k2 k3 k4 k5(max 5 words) -> search and show each hit count with [lang] langage == Configuration: BotConfig = [ { :name => :GoogleBot, :ch => /.*/, :referer => 'http://rubyforge.org/projects/nadoka/', # Register URL at http://code.google.com/intl/ja/apis/ajaxsearch/signup.html # and set your URL to :referer and your API key to :api_key if you want. :api_key => nil, :googlec_maxwords => 5, :search_default_lang => 'ja', :count_default_lang => '', :ch_kcode => :tojis, }, ] =end require 'iconv' require 'kconv' require 'shellwords' require 'cgi' require 'open-uri' begin require 'json' rescue LoadError require 'rubygems' require 'json' end class GoogleBot < Nadoka::NDK_Bot def bot_initialize bot_init_utils @search_default_lang = (@bot_config[:search_default_lang] || 'ja').sub(/^lang_/, '') @googlec_maxwords = @bot_config[:googlec_maxwords] || 5 @count_default_lang = (@bot_config[:count_default_lang] || '').sub(/^lang_/, '') @referer = @bot_config[:referer] || 'http://rubyforge.org/projects/nadoka/' @api_key = @bot_config[:api_key] @uri_slog = @bot_config.fetch(:uri_slog, false) @ch_kcode = @bot_config.fetch(:ch_kcode, :tojis) end def on_privmsg prefix, ch, msg return unless @available_channel === ch return if same_bot?(ch) if response = dispatch_command(msg) send_notice(ch, response) end end SEARCHER = %w!web calc code local video blogs news books images ime imed patent!.freeze SEARCHER_RE = Regexp.new("(?:" + SEARCHER.join('|') + ")").freeze def search_searcher key SEARCHER.each{|searcher| if /\A#{key}/ =~ searcher return searcher end }; nil end def dispatch_command msg begin case msg when /^goo(o*)gle( #{SEARCHER_RE})?(:.*?)?>\s*(.+)/o, /^gu(u*)guru(#{SEARCHER_RE})?(:.+)?>\s*(.+)/o "goo#{$1}gle#{$2} bot#{$3}: #{search($1.length, $3, $4, $2)}" when /^googlec( #{SEARCHER_RE})?(:.*?)?>\s*(.+)/o "googlec#{$1} bot#{$2}: #{googlec($1, $3, $2)}" when /^g(\w+)?(:.*?)?>\s*(.+)/ searcher = $1 ? search_searcher($1) : 'web' "google #{searcher} bot#{$2}: #{search(0, $2, $3, searcher)}" if searcher end rescue Exception => e @manager.ndk_error e "google bot: #{e.class} (#{e.message} @ #{e.backtrace[0]})" end end def do_search word, cnt, lang, searcher='web' i = 0 begin uri = "http://ajax.googleapis.com/ajax/services/search/" uri << searcher uri << "?v=1.0&q=" uri << CGI.escape(word) if @api_key uri << "&key=#{CGI.escape(@api_key)}" end cnt = cnt.to_i if cnt > 0 uri << "&start=#{cnt.to_i}" end if lang uri << "&hl=#{CGI.escape(lang)}" if searcher == 'web' uri << "&lr=lang_#{CGI.escape(lang)}" end end @logger.slog "GoogleBot: #{uri}" if @uri_slog result = open(uri, "Referer" => @referer) do |f| JSON.parse(f.read) end def result.estimatedTotalResultsCount self["responseData"]["cursor"]["estimatedResultCount"] end result rescue Exception => e retry if (i+=1) < 5 raise end end def api_search word, cnt, lang, searcher result = do_search word, cnt, lang, searcher count = result.estimatedTotalResultsCount.to_i if count > 0 count = count.to_s.gsub(/(\d)(?=\d{3}+$)/, '\\1,') url = title = '' e = result["responseData"]["results"][0] url = e['unescapedUrl'] || e['url'] || e['postUrl'] title = show_char_code_and_erase_tag(e['titleNoFormatting']) "#{title} - #{url} (and #{count} hit#{(count.to_i > 1) ? 's' : ''})" else "no match" end end def google_calc exp @logger.slog("google_calc<#{exp.dump}") uri = "http://www.google.co.jp/search?ie=UTF8&oe=UTF-8&q=#{CGI.escape(exp)}" html = open(uri) do |f| f.read end if /class=r [^<>]+>(.+?)<\/b>/u =~ html result = $1 # @logger.slog("google_calc>#{result.dump}") result.gsub!(/(.+?)<\/sup>/u) { "^(#{$1})" } result.gsub!(/<.+?>/u, '') result.gsub!(/&\#215;/u, "\303\227") result.send(@ch_kcode) # NKF.nkf('-j', result) else "response error" end rescue Exception $!.to_s end def google_code key return "http://google.com/codesearch#search/&q=#{CGI.escape(key)}&ct=os" end if defined?(URI.encode_www_form) def encode_www_form(enum) URI.encode_www_form(enum) end else def encode_www_form(enum) enum.map do |k, v| "#{URI.encode(k)}=#{URI.encode(v)}" end.join('&') end end # see http://www.google.com/intl/ja/ime/cgiapi.html def google_ime text, d=false url = 'http://www.google.com/transliterate?' url << encode_www_form('langpair' => 'ja-Hira|ja', 'text' => text) data = open(url){|f| # TODO: gsub fix invalid JSON, should remove after fix response # see http://www.google.com/support/forum/p/ime/thread?tid=06501c8b7a16add3&hl=ja JSON.parse(f.read.gsub(/,(?=\n\])/,'')) } if d result = data.map do |org, candidates| "#{org}=#{candidates.join('/')}" end.join(' ') else result = data.map do |org, candidates| candidates[0] end.join('') end show_char_code_and_erase_tag(result) rescue Exception $!.to_s[/.+/] # first line end def search cnt, lang, word, searcher=nil lang = lang_check(lang) searcher = searcher_check(searcher) word = search_char_code(word) case searcher when 'code' google_code word when 'calc' google_calc word when 'ime' google_ime word when 'imed' google_ime word, true else api_search word, cnt, lang, searcher end end def googlec lang, word, searcher=nil lang = lang_check(lang, @count_default_lang) searcher = searcher_check(searcher) words = Shellwords.shellwords(word).map{|e| "\"#{e}\""} return 'too many options' if words.size > @googlec_maxwords words.map{|rw| w = search_char_code(rw) result = do_search "'#{w}'", 0, lang, searcher "#{rw}(#{result.estimatedTotalResultsCount.to_s.gsub(/(\d)(?=\d{3}+$)/, '\\1,')})" }.join(', ') end def erase_tag str CGI.unescapeHTML(str.gsub(/\<.+?\>/, '')) end def lang_check lang, default = @search_default_lang if !lang @search_default_lang else lang = lang[1..-1] if lang.empty? nil elsif /^lang_/ =~ lang lang.sub(/^lang_/, '') else lang end end end def searcher_check searcher if !searcher 'web' else searcher = searcher.strip if SEARCHER.include?(searcher) searcher else 'web' end end end def show_char_code_and_erase_tag str return CGI.unescapeHTML(erase_tag(str.toeuc)).send(@ch_kcode) case $KCODE when 'EUC', 'SJIS' CGI.unescapeHTML(str.gsub(/\<.+?\>/, '')).send(@ch_kcode) when 'NONE', 'UTF-8' begin str = Iconv.conv("EUC-JP", "UTF-8", str) CGI.unescapeHTML(str.gsub(/\<.+?\>/, '')).send(@ch_kcode) rescue => e "(char code problem: #{e.class}[#{e.message.dump}])" end else str end end def search_char_code str case $KCODE when 'EUC', 'SJIS' str.toeuc when 'NONE' begin Iconv.conv("UTF-8", "EUC-JP", str.toeuc) rescue => e raise "(char code problem: #{e.class})" end when 'UTF-8' str else raise end end end nadoka-0.7.6/plugins/roulettebot.nb0000644000000000000000000000135411450645335016042 0ustar rootroot=begin This plugin is test version. =end require 'shellwords' require 'kconv' class RouletteBot < Nadoka::NDK_Bot def bot_initialize @available_channel = @bot_config[:ch] || /.*/ end def on_privmsg prefix, ch, msg if @available_channel === ch if /\Aroulette>\s*(.+)\s*/ =~ msg send_notice(ch, "roulette bot: #{randomize($1)[0].tojis}") elsif /\Ashuffle>\s*(.+)\s*/ =~ msg send_notice(ch, "shuffle bot: #{ randomize($1).join(' ').tojis}") elsif /\Arandom>\s*((\d+)|)/ =~ msg num = $2 ? $2.to_i : 1000 send_notice(ch, "random bot: #{prefix.nick} -> #{rand num}") end end end def randomize msgs res = Shellwords.shellwords(msgs.toeuc).sort_by{rand} end end nadoka-0.7.6/plugins/weba.nb0000644000000000000000000001231710566613051014406 0ustar rootroot# -*-ruby-*- # # Copyright (c) 2004-2005 SASADA Koichi # # 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: weba.nb 181 2007-02-20 15:39:21Z znz $ # =begin == Abstract WebA: Web Accessor http interface for irc You can access IRC via (()) (by default). == Configuration BotConfig = [ { :name => :WebA, :passwd => 'WebAPassWord', # if passwd is specified, use Basic Access Authentication with id :id => 'weba', :port => 12345, :entry => 'weba', # :message_format => ... (see source) } ] =end require 'webrick' require 'lib/tagparts' class WebA < Nadoka::NDK_Bot class WebAlet < WEBrick::HTTPServlet::AbstractServlet def initialize server, bot, authorizer super @bot = bot @auth = authorizer end def do_GET req, res @auth.authenticate(req, res) if @auth begin res.body = @bot.htmlpage(req.query).to_s res['content-type'] = 'text/html; charset=Shift_JIS' rescue WebARedirect => e res.set_redirect(WEBrick::HTTPStatus::Found, "#{req.path}?ch=#{URI.encode(e.ch.tosjis)}") res.body = 'moved' end end end class WebARedirect < Exception attr_reader :ch def initialize ch @ch = ch end end include HTMLParts def htmlpage query rch = (query['ch'] || '').tojis ch = ccn(rch) ch = !ch.empty? && (@state.channels.include?(ch) || ch == 'all') && ch ttl = ch ? " - #{rch.tosjis}" : '' if ch && (msg = query['message']) && !msg.empty? msg = msg.tojis + ' (from WebA)' send_privmsg(ch, msg) raise WebARedirect.new(ch) end _html( _head(_title("WebA: IRC Web Accessor#{ttl}")), _body( _h1("WebA#{ttl}"), _p( _a({:href => "./#{@entry}?ch="+URI.encode((rch || '').tosjis)}, 'reload'), _a({:href => "./#{@entry}"}, 'top') ), case ch when 'command' command_ch else view_ch(rch, ch) select_ch(rch, ch) end )) end def select_ch rch, ch _p({:class => 'channel-list'}, (@state.channel_raw_names.sort + ['all']).map{|e| e = e.tosjis [_a({:href => "./#{@entry}?ch="+ URI.encode(e)}, e), ' '] } ) end def view_ch rch, ch return unless ch chs = ch.tosjis if ch == 'all' msgs = [] @stores.pools.each{|_, store| msgs.concat store.pool } msgs = msgs.sort_by{|msg| msg[:time]} else msgs = (@stores.pools[ch] && @stores.pools[ch].pool) || [] end _div({:class => 'irc-accessor'}, if(ch != 'all') _form({:method => 'get', :action => "./#{@entry}", :class => 'sayform'}, "msg: ", _input({:type => 'text', :name => 'message', :class => 'msgbox'}), _input({:type => 'submit', :name => 'say', :value => 'say'}), _input({:type => 'hidden', :name => 'ch', :value => ch}) ) end, _h2("channel #{ch.tosjis}"), _div({:class => 'messages'}, msgs.map{|m| if ch == 'all' && m[:ch] chn = _a({:href => "./#{@entry}?ch=#{URI.encode(m[:ch])}"}, m[:ch].tosjis) msg = @config.log_format_message(@all_message_format, m) elsif m[:type] == 'PRIVMSG' chn = _a({:href => "./#{@entry}?user=#{URI.encode(m[:user])}"}, "<#{m[:user].tosjis}>") msg = @config.log_format_message(@message_format, m) else msg = @config.log_format_message(@message_format, m) chn = '' end _div({:class=>'msg'}, "#{m[:time].strftime('%H:%M')} ", chn, "#{msg}".tosjis) }.reverse ) ) end def bot_initialize @stores = @logger.message_stores @server = WEBrick::HTTPServer.new({ :Port => @bot_config.fetch(:port, 12345), }) @entry = @bot_config.fetch(:entry, 'weba') auth = nil if passwd = @bot_config.fetch(:passwd, 'WebAPassWord') id = @bot_config.fetch(:id, 'weba') userdb = Hash.new userdb.extend(WEBrick::HTTPAuth::UserDB) userdb.auth_type = WEBrick::HTTPAuth::BasicAuth userdb.set_passwd("WebA Authentication", id, passwd) auth = WEBrick::HTTPAuth::BasicAuth.new({ :Realm => "WebA Authentication", :UserDB => userdb, :Algorithm => 'MD5-sess', :Qop => [ 'auth' ], :UseOpaque => true, :NonceExpirePeriod => 60, :NonceExpireDelta => 5, }) end @server.mount("/#{@entry}", WebAlet, self, auth) @server_thread = Thread.new{ begin @server.start rescue Exception => e @manager.ndk_error e end } @message_format = @config.default_log[:message_format].merge( @bot_config.fetch(:message_format, { 'PRIVMSG' => '{msg}', 'NOTICE' => '{{user}} {msg}', })) @all_message_format = @config.default_log[:message_format].merge( @bot_config.fetch(:all_message_format, {})) end def bot_destruct @server_thread.kill @server.shutdown sleep 1 end end nadoka-0.7.6/plugins/evalbot.nb0000644000000000000000000000217610566613051015126 0ustar rootroot# -*-ruby-*- # # Copyright (c) 2004-2005 SASADA Koichi # # 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. # # # ruby evaluator on IRC # # $Id: evalbot.nb 181 2007-02-20 15:39:21Z znz $ # require 'timeout' class EvalBot < Nadoka::NDK_Bot EvalNick = 'nadoka_eval' def on_client_privmsg client, ch, message if ch == EvalNick ans = eval_message(message) msg = Cmd.privmsg(@state.nick, 'ans: ' + ans) client.send_to_client client.add_prefix(msg, EvalNick) raise ::Nadoka::NDK_BotSendCancel end end def on_nadoka_command client, command, *params if command == 'eval' msg = Cmd.privmsg(@state.nick, 'Hello, this is ruby evaluator') client.send_to_client client.add_prefix(msg, EvalNick) raise ::Nadoka::NDK_BotSendCancel end end def eval_message message begin ans = Thread.new{ $SAFE = 4 timeout(3){ Kernel.eval(message) } }.value.to_s rescue Exception => e ans = e.message end end end nadoka-0.7.6/plugins/messagebot.nb0000644000000000000000000000435610403753065015625 0ustar rootroot# -*-ruby-*- # # Copyright (C) 2004 Kazuhiro NISHIYAMA # All rights reserved. # This is free software with ABSOLUTELY NO WARRANTY. # # You can redistribute it and/or modify it under the terms of # the Ruby's licence. # =begin BotConfig = [ { :name => :MessageBot, :message_file => 'message.yaml', :root_key => Setting_name, :channels => %w[#nadoka #Ruby:*.jp], }, ] =end require 'nkf' require 'time' require 'yaml/store' class MessageBot < Nadoka::NDK_Bot def bot_initialize @store = YAML::Store.new(@bot_config[:message_file]) @root_key = @bot_config[:root_key] @channels = @bot_config[:channels].collect{|ch| ch.downcase } load_message end def load_message @store.transaction do |db| if db.root?(@root_key) h = db[@root_key] @list = h['list'] || Hash.new @message = h['message'] || Hash.new else @list = Hash.new @message = Hash.new end end end def save_message @store.transaction do |db| db[@root_key] = { 'list' => @list, 'message' => @message, } end end def on_privmsg prefix, ch, msg user = prefix.nick c = NKF.nkf('-e', ch.to_s).downcase return unless @channels.include?(c) u = user.downcase now = Time.now key = "#{c} #{u}" message_id = "<#{now.strftime('%Y%m%d%H%M%S')}.#{now.usec}.#{u}@#{c}>" if @list.key?(key) message_id_list = @list[key] message_id_list.each do |message_id| h = @message[message_id] next if h.key?('delivered') message = "#{h['from']}さんから#{h['to']}さんへ伝言「#{h['body']}」" send_notice(ch, NKF.nkf('-Ej -m0', message)) @message[message_id]['delivered'] = now end @list.delete(key) save_message end if /^伝言 (\S+) (.+)$/e =~ NKF.nkf('-e -m0', msg.to_s) to_nick, body = $1, $2 @message[message_id] = { 'from' => user, 'to' => to_nick, 'date' => now, 'channel' => ch, 'body' => body, } key = "#{c} #{to_nick.downcase}" @list[key] ||= [] @list[key].push(message_id) save_message send_notice(ch, NKF.nkf('-Ej -m0', "#{$1}さんへの伝言を承りました > #{u}さん")) end end end nadoka-0.7.6/plugins/sixamobot.nb0000644000000000000000000000320010566613051015464 0ustar rootroot#-*-ruby-*- # # Copyright (c) 2004-2005 SASADA Koichi # # 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. # # # Sixamo bot # # add config like this: # =begin BotConfig = [ { :name => :SixamoBot, :dir => '~/sixamo', :ch => '#nadoka', :tm => 10, :id => /(sixamo)|(ししゃも)/, :rt => [10, 5, 4, 4, 3, 2], } =end # # $Id: sixamobot.nb 181 2007-02-20 15:39:21Z znz $ # require 'sixamo' require 'kconv' class SixamoBot < Nadoka::NDK_Bot def bot_initialize @sixamo_dir = @bot_config[:dir] || '~/sixamo' @sixamo_ch = @bot_config[:ch] || '#nadoka' @sixamo_tm = @bot_config[:tm] || 10 @sixamo_id = @bot_config[:id] || /(sixamo)|(ししゃも)/ @sixamo_rt = @bot_config[:rt] || [10, 5, 4, 4, 3, 2] @prev = Time.now make_sixamo end def make_sixamo @sixamo = Sixamo.new(File.expand_path(@sixamo_dir)) end def on_privmsg prefix, ch, msg return unless @sixamo_ch === ch begin msg = Kconv.toeuc(msg) @sixamo.memorize msg unless @sixamo_id === msg rnd = case Time.now - @prev when 0..10; @sixamo_rt[0] when 10..20; @sixamo_rt[1] when 20..30; @sixamo_rt[2] when 30..60; @sixamo_rt[3] when 60..120;@sixamo_rt[4] else ; @sixamo_rt[5] end return if Kernel.rand(rnd) != 1 end @prev = Time.now talk = @sixamo.talk(msg) @sixamo.memorize talk send_notice ch, 'sixamo: ' + talk.tojis rescue make_sixamo end end end nadoka-0.7.6/plugins/shellbot.nb0000644000000000000000000000242610566613051015304 0ustar rootroot# -*-ruby-*- # # Copyright (c) 2004-2005 SASADA Koichi # # 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. # # # shell command bot # # $Id: shellbot.nb 181 2007-02-20 15:39:21Z znz $ # require 'timeout' require 'kconv' class ShellBot < Nadoka::NDK_Bot ShellNick = 'nadoka_shell' def on_client_privmsg client, ch, message if ch == ShellNick ans = exec_shell(message) ans.each{|line| msg = Cmd.privmsg(@state.nick, 'ans: ' + line) client.send_to_client client.add_prefix(msg, ShellNick) } raise ::Nadoka::NDK_BotSendCancel end end def on_nadoka_command client, command, *params if command == 'shell' msg = Cmd.privmsg(@state.nick, 'Hello, this is shell command executor') client.send_to_client client.add_prefix(msg, ShellNick) raise ::Nadoka::NDK_BotSendCancel end end def exec_shell message begin ans = Thread.new{ begin timeout(3){ str = `#{message}`.to_s str.tojis } rescue Exception => e e.message end }.value rescue Exception => e ans = e.message end end end nadoka-0.7.6/plugins/rss_checkbot.nb0000644000000000000000000000523711336126747016153 0ustar rootroot# -*-ruby-*- # # Copyright (c) 2004-2005 SASADA Koichi # # 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: rss_checkbot.nb 228 2010-02-15 02:13:59Z znz $ # =begin configuration like follows: BotConfig = [ { :name => :RSS_CheckBot, :rss_paths => [ 'http://www.ruby-lang.org/ja/index.rdf', ], :cache => "./rss-cache", :ch => '#nadoka_check', :tm => 30 # check interval time(minute) :over_message => nil # } =end require 'lib/rss_check' require 'iconv' require 'kconv' class RSS_CheckBot < Nadoka::NDK_Bot def bot_initialize @cache = File.expand_path(@bot_config.fetch(:cache, '~/.rss_check')) @paths = @bot_config.fetch(:rss_paths, ['http://www.ruby-lang.org/ja/index.rdf']) @ch = @bot_config.fetch(:ch, '#nadoka_check') @tm = @bot_config.fetch(:tm,30) # min @over = @bot_config.fetch(:over_message, nil) @rssc = RSS_Check.new(@paths, @cache, true) @ic = Iconv.open("EUC-JP", "UTF-8") @prevtm= Time.now end def bot_state nt = Time.at(@prevtm.to_i + @tm * 60) "<#{self.class}: next check at #{nt.asctime}@#{@ch}>" end def __on_privmsg prefix, ch, msg if /^rss> check/ =~ msg && ch == @ch && prefix.nick == @state.nick make_notice Time.now end end def send_notice(ch, msg) if ch.respond_to? :each ch.each{|c| super(c, msg) sleep 5 # Flood Protection } else super(ch, msg) sleep 5 # Flood Protection end end def on_timer tm check end def check tm = Time.now if tm.to_i - @tm * 60 > @prevtm.to_i make_notice tm end end def make_notice tm @prevtm = tm begin items = @rssc.check @rssc.save rescue => e send_notice(@ch, "rss bot error: #{e}") @manager.ndk_error e return end make_notice_thread items end def make_notice_thread items Thread.new{ begin items.each{|e| if e[:ccode] == 'UTF-8' begin title = e[:title].gsub(/\357\275\236/u, "\343\200\234") title = @ic.iconv(title).tojis rescue Exception # maybe, char code translation error next end else title = e[:title].tojis end send_notice(@ch, "rss bot: #{title} - #{e[:about]}") } rescue Exception => e send_notice(@ch, "rss bot error: #{e}") @manager.ndk_error e end send_notice(@ch, "rss bot: #{@over}") if @over } end end nadoka-0.7.6/plugins/backlogbot.nb0000644000000000000000000000454511450645335015606 0ustar rootroot# -*-ruby-*- # # Copyright (c) 2004-2005 SASADA Koichi # # 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: backlogbot.nb 240 2010-09-29 14:19:09Z znz $ # =begin == Abstract BackLogBot support rich backlog management scheme. == Configuration # # Maybe you don't have to write config(default setting is very useful) # BotConfig = [ { :name => :BackLogBot, :clear => false, # if true, clear backlogs when output once :talker => false, # if true, talker will be pseudo talker :prefix => 'BL', :sender => 'backlogbot', :time_format => '%m/%d-%H:%M', } ] =end class BackLogBot < Nadoka::NDK_Bot def bot_initialize @stores = @logger.message_stores @talker = @bot_config.fetch(:talker, false) @clear = @bot_config.fetch(:clear, false) @prefix = @bot_config.fetch(:prefix, 'BL') @sender = @bot_config.fetch(:sender, 'backlogbot') @sepr = @bot_config.fetch(:separate, true) @tmfmt = @bot_config.fetch(:time_format, '%m/%d-%H:%M') @msgfmts= @bot_config.fetch(:message_format, @config.default_log[:message_format]) @pattern= @bot_config.fetch(:pattern, nil) end def channel_message? ch /\A[\&\#\+\!]/ =~ ch end def on_client_login client_count, client @stores.each_channel_pool{|ch, msgobjs| if @state.current_channels[ch] msgobjs.each{|msgobj| msg = @config.log_format_message(@msgfmts, msgobj) rch = msgobj[:ch] time = msgobj[:time] cmd = Cmd.notice(rch, "#{@prefix}(#{time.strftime(@tmfmt)}) #{msg}") client.add_prefix(cmd, @talker ? nick : @sender) client.send_msg cmd } else msgobjs.each{|msgobj| if ch == :__talk__ msg = @config.log_format_message(@config.talk_log[:message_format], msgobj) else msg = @config.log_format_message(@config.system_log[:message_format], msgobj) end next unless @pattern.nil? || @pattern =~ msg rch = msgobj[:ch] time = msgobj[:time] cmd = Cmd.notice(@state.nick, "#{@prefix}(#{time.strftime(@tmfmt)}) #{msg}") client.send_msg cmd } end } @stores.clear if @clear end end