require 'rbot/irc'
require 'rbot/rfc2812'
-require 'rbot/keywords'
require 'rbot/ircsocket'
-# require 'rbot/auth'
require 'rbot/botuser'
require 'rbot/timer'
require 'rbot/plugins'
:default => 4, :validate => Proc.new{|v| v >= 0},
:desc => "(flood prevention) max lines to burst to the server before throttling. Most ircd's allow bursts of up 5 lines",
:on_change => Proc.new {|bot, v| bot.socket.sendq_burst = v })
- BotConfig.register BotConfigStringValue.new('server.byterate',
- :default => "400/2", :validate => Proc.new{|v| v.match(/\d+\/\d/)},
- :desc => "(flood prevention) max bytes/seconds rate to send the server. Most ircd's have limits of 512 bytes/2 seconds",
- :on_change => Proc.new {|bot, v| bot.socket.byterate = v })
BotConfig.register BotConfigIntegerValue.new('server.ping_timeout',
:default => 30, :validate => Proc.new{|v| v >= 0},
:on_change => Proc.new {|bot, v| bot.start_server_pings},
Dir.mkdir("#{botclass}/logs") unless File.exist?("#{botclass}/logs")
Dir.mkdir("#{botclass}/registry") unless File.exist?("#{botclass}/registry")
+ Dir.mkdir("#{botclass}/safe_save") unless File.exist?("#{botclass}/safe_save")
+ Utils.set_safe_save_dir("#{botclass}/safe_save")
@ping_timer = nil
@pong_timer = nil
@timer = Timer::Timer.new(1.0) # only need per-second granularity
@save_mutex = Mutex.new
@timer.add(@config['core.save_every']) { save } if @config['core.save_every']
+ @quit_mutex = Mutex.new
@logs = Hash.new
@httputil = Utils::HttpUtil.new(self)
- @lang = Language::Language.new(@config['core.language'])
-
- # @keywords = Keywords.new(self)
+ @plugins = nil
+ @lang = Language::Language.new(self, @config['core.language'])
begin
@auth = Auth::authmanager
}
@client[:privmsg] = proc { |data|
m = PrivMessage.new(self, server, data[:source], data[:target], data[:message])
- debug "Message target is #{data[:target].inspect}"
- debug "Bot is #{myself.inspect}"
+ # debug "Message source is #{data[:source].inspect}"
+ # debug "Message target is #{data[:target].inspect}"
+ # debug "Bot is #{myself.inspect}"
# TODO use the new Netmask class
# @config['irc.ignore_users'].each { |mask| return if Irc.netmaskmatch(mask,m.source) }
def got_sig(sig)
debug "received #{sig}, queueing quit"
$interrupted += 1
+ quit unless @quit_mutex.locked?
debug "interrupted #{$interrupted} times"
- if $interrupted >= 5
+ if $interrupted >= 3
debug "drastic!"
log_session_end
exit 2
- elsif $interrupted >= 3
- debug "quitting"
- quit
end
end
rescue => e
raise e.class, "failed to connect to IRC server at #{@config['server.name']} #{@config['server.port']}: " + e
end
+ quit if $interrupted > 0
@socket.emergency_puts "PASS " + @config['server.password'] if @config['server.password']
@socket.emergency_puts "NICK #{@config['irc.nick']}\nUSER #{@config['irc.user']} 4 #{@config['server.name']} :Ruby bot. (c) Tom Gilbert"
+ quit if $interrupted > 0
start_server_pings
end
@timer.start
while @socket.connected?
+ quit if $interrupted > 0
if @socket.select
break unless reply = @socket.gets
@client.process reply
end
- quit if $interrupted > 0
end
# I despair of this. Some of my users get "connection reset by peer"
rescue SystemExit
log_session_end
exit 0
- rescue Errno::ETIMEDOUT, TimeoutError, SocketError => e
+ rescue Errno::ETIMEDOUT, Errno::ECONNABORTED, TimeoutError, SocketError => e
error "network exception: #{e.class}: #{e}"
debug e.backtrace.join("\n")
rescue BDB::Fatal => e
# relevant say() or notice() methods. This one should be used for IRCd
# extensions you want to use in modules.
def sendmsg(type, where, message, chan=nil, ring=0)
- # limit it according to the byterate, splitting the message
- # taking into consideration the actual message length
- # and all the extra stuff
+ # Split the message so that each line sent is not longher than 510 bytes
# TODO allow something to do for commands that produce too many messages
# TODO example: math 10**10000
- left = @socket.bytes_per - type.length - where.to_s.length - 4
+ left = 510 - type.length - where.to_s.length - 3
begin
if(left >= message.length)
sendq "#{type} #{where} :#{message}", chan, ring
# disconnect from the server and cleanup all plugins and modules
def shutdown(message = nil)
- debug "Shutting down ..."
- ## No we don't restore them ... let everything run through
- # begin
- # trap("SIGINT", "DEFAULT")
- # trap("SIGTERM", "DEFAULT")
- # trap("SIGHUP", "DEFAULT")
- # rescue => e
- # debug "failed to restore signals: #{e.inspect}\nProbably running on windows?"
- # end
- message = @lang.get("quit") if (message.nil? || message.empty?)
- if @socket.connected?
- debug "Clearing socket"
- @socket.clearq
- debug "Sending quit message"
- @socket.emergency_puts "QUIT :#{message}"
- debug "Flushing socket"
- @socket.flush
- debug "Shutting down socket"
- @socket.shutdown
+ @quit_mutex.synchronize do
+ debug "Shutting down ..."
+ ## No we don't restore them ... let everything run through
+ # begin
+ # trap("SIGINT", "DEFAULT")
+ # trap("SIGTERM", "DEFAULT")
+ # trap("SIGHUP", "DEFAULT")
+ # rescue => e
+ # debug "failed to restore signals: #{e.inspect}\nProbably running on windows?"
+ # end
+ message = @lang.get("quit") if (message.nil? || message.empty?)
+ if @socket.connected?
+ debug "Clearing socket"
+ @socket.clearq
+ debug "Sending quit message"
+ @socket.emergency_puts "QUIT :#{message}"
+ debug "Flushing socket"
+ @socket.flush
+ debug "Shutting down socket"
+ @socket.shutdown
+ end
+ debug "Logging quits"
+ server.channels.each { |ch|
+ irclog "@ quit (#{message})", ch
+ }
+ debug "Saving"
+ save
+ debug "Cleaning up"
+ @save_mutex.synchronize do
+ @plugins.cleanup
+ end
+ # debug "Closing registries"
+ # @registry.close
+ debug "Cleaning up the db environment"
+ DBTree.cleanup_env
+ log "rbot quit (#{message})"
end
- debug "Logging quits"
- server.channels.each { |ch|
- irclog "@ quit (#{message})", ch
- }
- debug "Saving"
- save
- debug "Cleaning up"
- @plugins.cleanup
- # debug "Closing registries"
- # @registry.close
- debug "Cleaning up the db environment"
- DBTree.cleanup_env
- log "rbot quit (#{message})"
end
# message:: optional IRC quit message
exec($0, *@argv)
end
- # call the save method for bot's config, keywords, auth and all plugins
+ # call the save method for all of the botmodules
def save
@save_mutex.synchronize do
- # @config.save
- # @keywords.save
- # @auth.save
@plugins.save
DBTree.cleanup_logs
end
end
- # call the rescan method for the bot's lang, keywords and all plugins
+ # call the rescan method for all of the botmodules
def rescan
- @lang.rescan
- @plugins.rescan
- # @keywords.rescan
+ @save_mutex.synchronize do
+ @lang.rescan
+ @plugins.rescan
+ end
end
# channel:: channel to join
sendq "MODE #{channel} #{mode} #{target}", channel, 2
end
+ # kicking a user
+ def kick(channel, user, msg)
+ sendq "KICK #{channel} #{user} :#{msg}", channel, 2
+ end
+
# m:: message asking for help
# topic:: optional topic help is requested for
# respond to online help requests