#!/usr/local/bin/ruby class AIMBot ADMINS = ["sethkills"] TACCMD = "tclsh8.3 /usr/local/libexec/tac-0.15-bot/tac.tcl" SIGNON_RE = /\.\.\.Welcome to AOL Instant Messenger \(Tac \d\.\d+\)\.\.\./ DROPMSG = "A message has been dropped, you are exceeding the server speed limit." TMOUT = 4 # TACCMD = "tac" def initialize(sn = nil, pw = nil) @ch = Hash.new(false) @p = IO::popen(TACCMD, 'r+') wait_raw("login: ") if sn then puts sn else sn = gets.chomp end @sn = sn @nsn = AIMBot::norm(@sn) @p.puts(@sn) wait_raw("Password:") pw = getpass("") unless pw puts @p.puts(pw) wait(" ") wait(SIGNON_RE) trap "INT", 'destroy' end def die(s) $stderr.puts "Fatal error: #{s}." exit 1 end def wait_raw(s) $_ = @p.read(s.length) die("Waiting (raw) for \"#{s}\", got \"#{$_}\"") if $_ != s print $_ $_ end def wait(re) @p.gets chomp! if re puts $_ $_ else die("Waiting for \"#{re.source}\", got \"#{$_}\"") end end def getpass(s = nil) tty = open("/dev/tty", "r+") if s tty.write(s) tty.flush end begin system("stty", "-echo") ret = gets tty.print("") ret ensure system("stty", "echo") end end def AIMBot.norm(s) s.downcase.gsub(/ +/, "") end def AIMBot.is_admin?(s) ADMINS.include?(AIMBot::norm(s)) end def cmd(s, av = nil) s+= " " + av.join(" ") if av puts s @p.puts(s) end def join(c) cmd("/cj", [c]) end def part(c) cmd("/cl", [c]) end def im(sn, m) cmd("/m", [AIMBot::norm(sn), m]) end def chat_to_num(c) n = @ch[AIMBot::norm(c)] die("No chat matches name \"#{c}\".") unless n n end def chat(c, m) cmd("c#{chat_to_num(c)}", [m]) end def run sc = 0 while @p.gets chop! puts $_ if /^<([^:]+):([^>]+)> (.*)$/ next if AIMBot::norm($2) == @nsn if sc > 0 sc-= 1 next end elsif /^Chat c(\d+) is now (.*)\.$/ @ch[AIMBot::norm($2)] = $1.to_i elsif /^You have left chat (.*)\.$/ @ch.delete(AIMBot::norm($1)) elsif /^<\*([^*]+)\*> \.(\w+)\s?(.*)?$/ if sc > 0 sc-= 1 next end if AIMBot::is_admin?($1) case $2 when "join" im($1, "Missing argument") unless $3 join($3) when "part" im($1, "Missing argument") unless $3 part($3) when "quit" im($1, "Shutting down...") close return else im($1, "Unknown command #{$2}") end else im($1, "You are not authorized to perform " + "administrative commands") end next end die($_) if $_ == "Connection Closed" if $_ == DROPMSG sleep(TMOUT) sc = 5 end yield($_) end end def close cmd("/q") @p.close end end