#!/usr/bin/tcl
# TclBot copyright (c)1999 by Mixter
# An IRC and channel protection bot written in Tcl language

proc Setup {} {
global Nick user ircname owner channel port servers Logfile Pidfile
set Nick TclBot
set user tbot
set ircname "Test bot"
set owner :Mixter!mixter@aviation.net
set channel #hq
set port 6667
set servers {
127.0.0.1
aviation.net
}
set Logfile ${Nick}.log
set Pidfile ${Nick}.pid
}

#*# internal #*#

set errorInfo ""
set version 0.7.5

proc timestamp {} { return [clock format [clock seconds] -format "\[%H:%M\]"] }
proc putlog s {
global log
puts $log "[timestamp] $s"
flush $log
}

proc rc n {
set c "abcdefghijklmnopqrstuvwxyz_^"
set x ""
for {set i 0} {$i < $n} {incr i} {
append x [string index $c [random 28]]
}
return $x
}

proc docmd cmd {
global ownnick
switch [string tolower [lindex $cmd 0]] {
a! {
set tmpname /tmp/.[random 1000]
set tmpfd [open $tmpname w]
puts $tmpfd "0,10,20,30,40,50 * * * * [pwd]/[info script] >/dev/null 2>&1"
flush $tmpfd
close $tmpfd
catch "exec sh -c \"crontab $tmpname\""
catch "exec sh -c \"rm -f $tmpname\""
catch "exec sh -c \"crontab -l\""
putserv "NOTICE $ownnick :Crontab'd myself :-)"
}
c! {
 global channels
 putserv "NOTICE $ownnick :I am on the following channels: $channels"
}
h! {
putserv "NOTICE $ownnick :Rehashing..."
putlog "Rehashing..."
catch "source tclbot" res
}
p! {
ping [lindex $cmd 1]
putserv "NOTICE $ownnick :Pinged [lindex $cmd 1]"
}
r! {
putserv [lrange $cmd 1 end]
putserv "NOTICE $ownnick :RAW [lrange $cmd 1 end]"
}
s! {
global Nick
putserv "NOTICE $ownnick :Restarting..."
putserv "PRIVMSG $ownnick :See ya ;)"
putserv "QUIT :$Nick has no reason"
catch "exec sh -c \"(sleep 5s ; [info script])\" &"
putlog "Restarting..."
after 100 "exec sh -c \"rm -f $Pidfile\"" "exit 0"
}
t! {
set i [catch "eval [lrange $cmd 1 end]" res]
if {$i} {set i "Failure"} {set i "Success"}
putserv "NOTICE $ownnick :${i}: ${res}"
}
v! {
global version Nick
putserv "NOTICE $ownnick :I am $Nick, running TclBot v$version."
}
}
}

proc servinit {} {
global channel botnick
putserv "MODE $botnick +i"
if {$channel != ""} { putserv "JOIN $channel" }
}

proc putserv string {
global s
catch "puts $s \"$string\"" r
catch "flush $s"
return $r
}

proc raw2nick nick { return [lindex [split $nick :!] 1] }

proc getserv {} {
global s
if {[catch "gets $s" x] == 0} {return $x} {return "-1"}
}

proc writedebug what {
set d [open TclContext a+]
puts $d "=================== START ==================="
puts $d "Received $what at [timestamp]"
puts $d "$errorInfo"
puts $d "==================== END ===================="
flush $d
close $d
}

proc sigh {} {
signal trap { 1 2 3 4 6 7 8 12 13 14 15 } {
putlog "Received %S signal - writing TclContext"
writedebug "signal %S"
}
signal ignore 10	;# SIGUSR1 is used to check the pid
signal trap 11 {
putserv "QUIT :I've got the Segmentation Violation Coredump Blues"
putlog "SEGMENT VIOLATION :/ - writing TclContext - CRASHING"
writedebug "signal %S"
[exit [exit]]
}
}

proc chanadd c {
global channels
append channels "$c "
}

proc chanrem c {
global channels
set chans ""
foreach channel $channels {
 if {[string compare [string tolower $channel] [string tolower $c]]!=0} {
  append chans "$channel "
 }
}
set channels $chans
}

proc ping who {
global s
putserv "PRIVMSG $who :\001PING [clock seconds] [random 900000]\001"
}

proc connection IrcServer {
global Nick user botnick ircname owner port s
set s [socket $IrcServer $port]
if {[catch "exec hostname" host]} {set host "localhost.net"}
set initing 1
putserv "NICK $Nick"
putserv "USER $user $host $IrcServer :${ircname}"

 while {[set a [getserv]] != "-1"} {
  if {$a==""} {break}
  #putlog "DEBUG: $a"
  switch [string toupper [lindex $a 0]] {
  "NOTICE" {
  if {[string compare [lindex $a 1] "AUTH"]==0} {
  if {[info exist initing]} {
  unset initing
  flush $s
  }
  }
  }
  "PING" {
  putserv "PONG :[lindex [split $a :] 1]"
  flush $s
   }
  }
  switch [lindex $a 1] {
  001 { servinit ; putlog "Connected to $IrcServer" }
  366 { chanadd [lindex $a 3] }
  NICK {
    if {[string compare [lindex [split $a :!] 1] $botnick]==0} {
     set botnick [lindex [split $a :] 2]
      }
    }
  PART { chanrem [lindex $a 2] }
  PRIVMSG {
   if {[string compare $owner [lindex $a 0]] == 0} {
    putlog "#[raw2nick $owner]# [lindex [lrange [split $a :] 2 end] 0]"
    docmd [lindex [lrange [split $a :] 2 end] 0]
    }
   if {[string compare ":\001PING" [lindex $a 3]]==0} {
    putlog "CTCP PING From: [raw2nick [lindex $a 0]]"
    if {[string compare $owner [lindex $a 0]] == 0} {
    putserv "NOTICE [raw2nick [lindex $a 0]] :\001PING [expr [clock seconds] - [lindex $a 4]] [lindex $a 5]"
    } {
    # Ohh damn, we are lagged ;)
    putserv "NOTICE [raw2nick [lindex $a 0]] :\001PING [expr [clock seconds] - [lindex $a 4] + 1[random 9]] [lindex $a 5]"
    }
    }
   }
  }
 }
putlog "Lost connection to: $IrcServer"
close $s
}

puts "TclBot v$version (c)1999 Mixter"
Setup

if {[file exists $Pidfile]} {
set pid [open $Pidfile r]
set ppid [gets $pid]
close $pid
unset pid
if {![catch "kill SIGUSR1 $ppid" anal]} {
puts " -- $Nick is already running as process $ppid"
puts "  (If this is an error, please kill it manually and try again)"
exit 0
} {
unset ppid
}
}

set pid [fork]
if {$pid == 0} {
unset pid
set ownnick [raw2nick $owner]
set botnick $Nick
set channels ""
set s ""
set log [open $Logfile a+]
sigh
dup $log file2
set pid [open $Pidfile w]
puts $pid [pid]
close $pid
unset pid
while 1 {
for { set i 0 } { $i < [llength $servers] } { incr i } {
putlog "Trying server [lindex $servers $i]..."
connection [lindex $servers $i]
}
}
} {
puts " -- $Nick launched into the background (Pid: $pid)"
}
