$debug = false unless $debug
# print +message+ if debugging is enabled
def debug(message=nil)
- print "DEBUG: #{message}\n" if($debug && message)
+ stamp = Time.now.strftime("%Y/%m/%d %H:%M:%S")
+ print "D: [#{stamp}] #{message}\n" if($debug && message)
#yield
end
# bot's Language data
attr_reader :lang
- # bot's configured addressing prefixes
- attr_reader :addressing_prefixes
-
# channel info for channels the bot is in
attr_reader :channels
exit 2
end
- #botclass = "#{Etc.getpwnam(Etc.getlogin).dir}/.rbot" unless botclass
- botclass = "#{ENV['HOME']}/.rbot" unless botclass
+ botclass = "#{Etc.getpwuid(Process::Sys.geteuid)[:dir]}/.rbot" unless botclass
+ #botclass = "#{ENV['HOME']}/.rbot" unless botclass
+ botclass = File.expand_path(botclass)
@botclass = botclass.gsub(/\/$/, "")
unless FileTest.directory? botclass
end
Dir.mkdir("#{botclass}/logs") unless File.exist?("#{botclass}/logs")
+ Dir.mkdir("#{botclass}/registry") unless File.exist?("#{botclass}/registry")
@ping_timer = nil
- @timeout_timer = nil
+ @pong_timer = nil
+ @last_ping = nil
@startup_time = Time.new
@config = BotConfig.new(self)
# TODO background self after botconfig has a chance to run wizard
@socket.puts "PONG #{data[:pingid]}"
}
@client[:pong] = proc {|data|
- # cancel the timeout timer
- debug "got a pong from the server, cancelling timeout"
- remove_timeout_timer
+ @last_ping = nil
}
@client[:nick] = proc {|data|
sourcenick = data[:sourcenick]
}
@client[:unknown] = proc {|data|
#debug "UNKNOWN: #{data[:serverstring]}"
- log data[:serverstring], ":unknown"
+ log data[:serverstring], ".unknown"
}
end
# connect the bot to IRC
def connect
- trap("SIGTERM") { quit }
- trap("SIGHUP") { quit }
- trap("SIGINT") { quit }
+ begin
+ trap("SIGINT") { quit }
+ trap("SIGTERM") { quit }
+ trap("SIGHUP") { quit }
+ rescue
+ debug "failed to trap signals, probably running on windows?"
+ end
begin
@socket.connect
- rescue => e
+ rescue => e
raise "failed to connect to IRC server at #{@config['server.name']} #{@config['server.port']}: " + e
end
@socket.puts "PASS " + @config['server.password'] if @config['server.password']
# begin event handling loop
def mainloop
while true
- connect
- @timer.start
-
begin
+ connect
+ @timer.start
+
while true
if @socket.select
break unless reply = @socket.gets
# exceptions that ARENT SocketError's. How am I supposed to handle
# that?
#rescue TimeoutError, SocketError => e
+ rescue SystemExit
+ exit 0
rescue Exception => e
- puts "network exception: connection closed: #{e}"
+ puts "network exception: connection closed: #{e.inspect}"
puts e.backtrace.join("\n")
@socket.shutdown # now we reconnect
rescue => e
end
puts "disconnected"
+ @last_ping = nil
@channels.clear
@socket.clearq
# log message +message+ to a file determined by +where+. +where+ can be a
# channel name, or a nick for private message logging
def log(message, where="server")
- message.chomp!
+ message = message.chomp
stamp = Time.now.strftime("%Y/%m/%d %H:%M:%S")
+ where = where.gsub(/[:!?$*()\/\\<>|"']/, "_")
unless(@logs.has_key?(where))
@logs[where] = File.new("#{@botclass}/logs/#{where}", "a")
@logs[where].sync = true
# disconnect from the server and cleanup all plugins and modules
def shutdown(message = nil)
- trap("SIGTERM", "DEFAULT")
- trap("SIGHUP", "DEFAULT")
- trap("SIGINT", "DEFAULT")
+ begin
+ trap("SIGINT", "DEFAULT")
+ trap("SIGTERM", "DEFAULT")
+ trap("SIGHUP", "DEFAULT")
+ rescue
+ debug "failed to trap signals, probably running on windows?"
+ end
message = @lang.get("quit") if (message.nil? || message.empty?)
+ debug "Clearing socket"
@socket.clearq
+ debug "Saving"
save
+ debug "Cleaning up"
@plugins.cleanup
+ debug "Logging quits"
@channels.each_value {|v|
log "@ quit (#{message})", v.name
}
+ # debug "Closing registries"
+ # @registry.close
+ debug "Cleaning up the db environment"
+ DBTree.cleanup_env
+ debug "Sending quit message"
@socket.puts "QUIT :#{message}"
+ debug "Flushing socket"
@socket.flush
+ debug "Shutting down socket"
@socket.shutdown
- @registry.close
puts "rbot quit (#{message})"
end
# message:: optional IRC quit message
# quit IRC, shutdown the bot
def quit(message=nil)
- shutdown(message)
- exit 0
+ begin
+ shutdown(message)
+ ensure
+ exit 0
+ end
end
# totally shutdown and respawn the bot
# call the save method for bot's config, keywords, auth and all plugins
def save
- @registry.flush
@config.save
@keywords.save
@auth.save
@plugins.save
+ DBTree.cleanup_logs
end
# call the rescan method for the bot's lang, keywords and all plugins
def status
secs_up = Time.new - @startup_time
uptime = Utils.secs_to_string secs_up
- return "Uptime #{uptime}, #{@plugins.length} plugins active, #{@registry.length} items stored in registry, #{@socket.lines_sent} lines sent, #{@socket.lines_received} received."
+ # return "Uptime #{uptime}, #{@plugins.length} plugins active, #{@registry.length} items stored in registry, #{@socket.lines_sent} lines sent, #{@socket.lines_received} received."
+ return "Uptime #{uptime}, #{@plugins.length} plugins active, #{@socket.lines_sent} lines sent, #{@socket.lines_received} received."
end
# we'll ping the server every 30 seconds or so, and expect a response
# before the next one come around..
def start_server_pings
+ @last_ping = nil
# stop existing timers if running
unless @ping_timer.nil?
@timer.remove @ping_timer
@ping_timer = nil
end
- remove_timeout_timer
- timeout = @config['server.ping_timeout']
- return unless timeout > 0
- timeout = 30 if timeout > 30
+ unless @pong_timer.nil?
+ @timer.remove @pong_timer
+ @pong_timer = nil
+ end
+ return unless @config['server.ping_timeout'] > 0
# we want to respond to a hung server within 30 secs or so
@ping_timer = @timer.add(30) {
- remove_timeout_timer
+ @last_ping = Time.now
@socket.puts "PING :rbot"
- @timeout_timer = @timer.add_once(timeout) {
- debug "no PONG from server for #{timeout} seconds, reconnecting"
- begin
- @socket.shutdown
- rescue
- debug "couldn't shutdown connection (already shutdown?)"
+ }
+ @pong_timer = @timer.add(10) {
+ unless @last_ping.nil?
+ diff = Time.now - @last_ping
+ unless diff < @config['server.ping_timeout']
+ debug "no PONG from server for #{diff} seconds, reconnecting"
+ begin
+ @socket.shutdown
+ # TODO
+ # raise an exception to get back to the mainloop
+ rescue
+ debug "couldn't shutdown connection (already shutdown?)"
+ end
+ @last_ping = nil
end
- } if @config['server.ping_timeout']
+ end
}
end
private
- def remove_timeout_timer
- unless @timeout_timer.nil?
- @timer.remove(@timeout_timer)
- @timeout_timer = nil
- end
- end
-
# handle help requests for "core" topics
def corehelp(topic="")
case topic
return "say <channel>|<nick> <message> => say <message> to <channel> or in private message to <nick>"
when "action"
return "action <channel>|<nick> <message> => does a /me <message> to <channel> or in private message to <nick>"
- when "topic"
- return "topic <channel> <message> => set topic of <channel> to <message>"
+ # when "topic"
+ # return "topic <channel> <message> => set topic of <channel> to <message>"
when "quiet"
return "quiet [in here|<channel>] => with no arguments, stop speaking in all channels, if \"in here\", stop speaking in this channel, or stop speaking in <channel>"
when "talk"
end
if(m.address?)
+ delegate_privmsg(m)
case m.message
when (/^join\s+(\S+)\s+(\S+)$/i)
join $1, $2 if(@auth.allow?("join", m.source, m.replyto))
say $1, $2 if(@auth.allow?("say", m.source, m.replyto))
when (/^action\s+(\S+)\s+(.*)$/i)
action $1, $2 if(@auth.allow?("say", m.source, m.replyto))
- when (/^topic\s+(\S+)\s+(.*)$/i)
- topic $1, $2 if(@auth.allow?("topic", m.source, m.replyto))
+ # when (/^topic\s+(\S+)\s+(.*)$/i)
+ # topic $1, $2 if(@auth.allow?("topic", m.source, m.replyto))
when (/^mode\s+(\S+)\s+(\S+)\s+(.*)$/i)
mode $1, $2, $3 if(@auth.allow?("mode", m.source, m.replyto))
when (/^ping$/i)
say m.replyto, "pong"
when (/^rescan$/i)
if(@auth.allow?("config", m.source, m.replyto))
- m.okay
+ m.reply "Saving ..."
+ save
+ m.reply "Rescanning ..."
rescan
+ m.okay
end
when (/^quiet$/i)
if(auth.allow?("talk", m.source, m.replyto))
when (/^(hello|howdy|hola|salut|bonjour|sup|niihau|hey|hi(\W|$)|yo(\W|$)).*/i)
say m.replyto, @lang.get("hello_X") % m.sourcenick if(m.public?)
say m.replyto, @lang.get("hello") if(m.private?)
- else
- delegate_privmsg(m)
end
else
# stuff to handle when not addressed