require 'socket' class IrcClient attr_accessor :host, :port, :nick, :uhost, :chan, :timeout, :msg_interval def initialize(host, port = 6667) @host = host @port = port @uhost= 'rb rb rb :rb' @statusline1 = 'uncached' @statusline2 = 'uncached' # seconds @timeout = 300 # ping if no message during this, reconnect if no pong during this again @allow = Time.now - 10 @at = [] end def connect @sock = TCPSocket.open @host, @port @sentping = false @lastmsg = Time.now send "user #@uhost" send "nick #@nick" send "join #@chan" @chan = @chan.split(' ').first || @chan end def run connect loop do r, w, e = IO.select [@sock], nil, nil, 0.5 monitor if Time.now > @allow if not @at.empty? and [Time.now.hour, Time.now.min] == @at.first[0, 2] handle_sock "at #{@at.shift[2]}" end if not r handle_timeout if Time.now - @lastmsg >= @timeout elsif r.include? @sock @lastmsg = Time.now handle_sock @sock.gets.chomp end end end def send(l) if @allow > Time.now @allow = Time.now elsif @allow < Time.now - 10 @allow = Time.now - 10 end @allow += 2.1 puts "#{Time.now.strftime '%H:%M'} > #{l}" if $VERBOSE @sock.write l + "\r\n" end def handle_timeout if @sentping raise 'server ping timeout' if Time.now - @sentping >= @timeout else send 'ping :stillalive' @sentping = Time.now end end def pm l send "PRIVMSG #@chan :#{l.empty? ? ' ' : l[0, 512]}" end def handle_sock l puts "#{Time.now.strftime '%H:%M'} #{l}" if $VERBOSE and l !~ /^ping/i case l when /^(?::jj.*!.* PRIVMSG .* :(\w+):?|at) (\w+)( >>)?( ?.*)/ target, file, mode, val = $1, $2, $3, $4.strip return if target and @nick !~ /#{target}/ mode = mode ? 'a' : 'w' case file when 'at' if val =~ /(\d+)h(\d*) (.*)/ @at << [$1.to_i, $2.to_i, $3] pm "at ok" end when /^target(pv|esq|rm|arm|subj)/ fld = $1 idx = %w[pv esq rm arm subj].index(fld)+1 tg = File.read('targetid').split(';').map { |e| e.chomp } if val.empty? pm "#{fld} #{tg[idx]}" else val = eval(val) rescue nil tg[idx] = val File.open('targetid', 'w') { |fd| fd.puts tg.join(';') } pm "targetid #{tg.join(';')}" end when /todo|dest|target|goinfrlist/: File.open(file, mode) { |fd| fd.puts val } pm "#{file} #{mode} ok" when 'status' pm @statusline1 + ' ' + @statusline2 when 'quit' send 'quit requested' when 'update' system 'ruby update.rb update >>log_mh 2>&1' when 'play_now' if not fork if not fork system "ruby bot.rb oneshot #{val} >>log_mh 2>&1" end exit else Process.wait end when 'cat' begin File.open(val) { |fd| fd.each_line { |l| pm l.chomp } } pm '' if File.size(val) == 0 rescue pm "read #{val}: #$!" end else #pm "#{file} unknown" end when /^ping (.*)/i send "pong #$1" when /pong.*stillalive/i @sentping = nil end end attr_accessor :file, :filesize def monitor if File.size(@file) != @filesize File.open(@file, 'rb') { |fd| fd.pos = @filesize l = fd.gets if l and not l.empty? # no chomp, send newlines present in the file pm l.chomp case l when /pa .*px/ @statusline1 = l.chomp when /sleep .* => (.*)/ @statusline2 = l.chomp.sub('sleep ', '').sub(/\d+h\d+ => /, '') end end @filesize = fd.pos } end end end if __FILE__ == $0 require 'mountycreds' id, pass, passr = mountycreds if ARGV.empty? c = IrcClient.new $ircsrv c.chan = '#logchn' c.nick = 'b_' + id.to_s c.file = 'log_mh' c.filesize = [0, File.size(c.file) - 400].max c.run else $stdin.close rescue nil $stdout.close rescue nil $stderr.close rescue nil exit if fork # daemonize loop do exec "ruby #$0" if not pid = fork sleep 1200 Process.waitpid(pid) end end end