set host chiark
set port 6667
if {![info exists nick]} { set nick Blight }
+if {![info exists ownfullname]} { set ownfullname "here to Help" }
+set ownmailaddr blight@chiark.greenend.org.uk
+
+if {![info exists globalsecret]} {
+ set gsfile [open /dev/urandom r]
+ fconfigure $gsfile -translation binary
+ set globalsecret [read $gsfile 32]
+ binary scan $globalsecret H* globalsecret
+ close $gsfile
+ unset gsfile
+}
+
+proc try_except_finally {try except finally} {
+ global errorInfo errorCode
+ set er [catch { uplevel 1 $try } emsg]
+ if {$er} {
+ set ei $errorInfo
+ set ec $errorCode
+ if {[catch { uplevel 1 $except } emsg3]} {
+ append ei "\nALSO ERROR HANDLING ERROR:\n$emsg3"
+ }
+ }
+ set er2 [catch { uplevel 1 $finally } emsg2]
+ if {$er} {
+ if {$er2} {
+ append ei "\nALSO ERROR CLEANING UP:\n$emsg2"
+ }
+ return -code $er -errorinfo $ei -errorcode $ec $emsg
+ } elseif {$er2} {
+ return -code $er2 -errorinfo $errorInfo -errorcode $errorCode $emsg2
+ } else {
+ return $emsg
+ }
+}
proc sendout {command args} {
global sock
}
proc onread {args} {
- global sock
+ global sock nick
if {[gets $sock line] == -1} { set terminate 1; return }
regsub -all "\[^ -\176\240-\376\]" $line ? line
set org $line
if {[regexp -nocase {^:([^ ]+) (.*)} $line dummy prefix remain]} {
set line $remain
+ if {[regexp {^([^!]+)!} $prefix dummy maybenick] &&
+ "[irctolower $maybenick]" == "[irctolower $nick]"} return
} else {
set prefix {}
}
upvar 1 n n
if {![regexp {^([^!]+)!} $p dummy n]} { error "not from nick" }
check_nick $n
- if {"[irctolower $n]" == "[irctolower $nick]"} { error "from myself" }
+ if {"[irctolower $n]" == "[irctolower $nick]"} {
+ error "from myself" {} {}
+ }
}
proc showintervalsecs {howlong} {
}
}
+proc process_kickpart {chan user} {
+ check_nick $user
+ if {![ischan $chan]} { error "not a channel" }
+
+ upvar #0 nick_onchans($user) oc
+ set lc [irctolower $chan]
+ set oc [grep tc {"$tc" != "$lc"} $oc]
+}
+
+proc msg_KICK {p c chans users comment} {
+ set chans [split $chans ,]
+ set users [split $users ,]
+ if {[llength $chans] > 1} {
+ foreach chan $chans user $users { process_kickpart $chan $user }
+ } else {
+ foreach user $users { process_kickpart [lindex $chans 0] $user }
+ }
+}
+
+proc msg_KILL {p c user why} {
+ nick_forget $user
+}
+
+set nick_arys {onchans username}
+
+proc nick_forget {n} {
+ global nick_arys
+ foreach ary $nick_arys {
+ upvar #0 nick_${ary}($n) av
+ catch { unset av }
+ }
+}
+
proc msg_NICK {p c newnick} {
+ global nick_arys
prefix_nick
recordlastseen_n $n "changing nicks to $newnick" 0
recordlastseen_n $newnick "changing nicks from $n" 1
+ foreach ary $nick_arys {
+ upvar #0 nick_${ary}($n) old
+ upvar #0 nick_${ary}($newnick) new
+ if {[info exists new]} { error "nick collision ?! $ary $n $newnick" }
+ if {[info exists old]} { set new $old; unset old }
+ }
}
-proc msg_JOIN {p c chan} { recordlastseen_p $p "joining $chan" 1 }
-proc msg_PART {p c chan} { recordlastseen_p $p "leaving $chan" 1 }
-proc msg_QUIT {p c why} { recordlastseen_p $p "leaving ($why)" 0 }
+proc msg_JOIN {p c chan} {
+ prefix_nick
+ recordlastseen_n $n "joining $chan" 1
+ upvar #0 nick_onchans($n) oc
+ lappend oc [irctolower $chan]
+}
+proc msg_PART {p c chan} {
+ prefix_nick
+ recordlastseen_n $n "leaving $chan" 1
+ process_kickpart $chan $n
+}
+proc msg_QUIT {p c why} {
+ prefix_nick
+ recordlastseen_n $n "leaving ($why)" 0
+ nick_forget $n
+}
proc msg_PRIVMSG {p c dest text} {
prefix_nick
sendprivmsg $n "error: $rv"
} else {
manyset $rv priv_msgs pub_msgs priv_acts pub_acts
- foreach {td val} [list $n $priv_msgs $output $pub_msgs] {
+ foreach {td val} [list $n $priv_acts $output $pub_acts] {
foreach l [split $val "\n"] {
- sendprivmsg $td $l
+ sendaction $td $l
}
}
- foreach {td val} [list $n $priv_acts $output $pub_acts] {
+ foreach {td val} [list $n $priv_msgs $output $pub_msgs] {
foreach l [split $val "\n"] {
- sendaction $td $l
+ sendprivmsg $td $l
}
}
}
}
+proc msg_INVITE {p c n chan} {
+ after 1000 [list sendout JOIN $chan]
+}
+
+proc grep {var predicate list} {
+ set o {}
+ upvar 1 $var v
+ foreach v $list {
+ if {[uplevel 1 [list expr $predicate]]} { lappend o $v }
+ }
+ return $o
+}
+
+proc msg_353 {p c dest type chan nicklist} {
+ global names_chans nick_onchans
+ if {![info exists names_chans]} { set names_chans {} }
+ set chan [irctolower $chan]
+ lappend names_chans $chan
+ foreach n [array names nick_onchans] {
+ upvar #0 nick_onchans($n) oc
+ set oc [grep tc {"$tc" != "$chan"} $oc]
+ }
+ foreach n [split $nicklist { }] {
+ regsub {^[@+]} $n {} n
+ check_nick $n
+ if {![string length $n]} continue
+ upvar #0 nick_onchans($n) oc
+ lappend oc $chan
+ }
+}
+
+proc msg_366 {p c args} {
+ global names_chans nick_onchans
+ if {[llength names_chans] > 1} {
+ foreach n [array names nick_onchans] {
+ upvar #0 nick_onchans($n) oc
+ set oc [grep tc {[lsearch -exact $tc $names_chans] >= 0} $oc]
+ if {![llength $oc]} { nick_forget $n }
+ }
+ }
+ unset names_chans
+}
+
proc ta_nomore {} {
upvar 1 text text
if {[string length $text]} { error "too many parameters" }
proc ucmdr {priv pub args} {
return -code return [concat [list $priv $pub] $args]
}
-
+
+proc loadhelp {} {
+ global help_topics
+
+ catch { unset help_topics }
+ set f [open helpinfos r]
+ set lno 0
+ while {[gets $f l] >= 0} {
+ incr lno
+ if {[regexp {^#.*} $l]} {
+ } elseif {[regexp {^ *$} $l]} {
+ if {[info exists topic]} {
+ set help_topics($topic) [join $lines "\n"]
+ unset topic
+ unset lines
+ }
+ } elseif {[regexp {^!([-+._0-9a-z]*)$} $l dummy newtopic]} {
+ if {[info exists topic]} { error "help $newtopic while in $topic" }
+ set topic $newtopic
+ set lines {}
+ } elseif {[regexp {^[^!#]} $l]} {
+ set topic
+ lappend lines [string trimright $l]
+ } else {
+ error "eh ? $lno: $l"
+ }
+ }
+ if {[info exists topic]} { error "unfinished topic $topic" }
+}
+
def_ucmd help {
- ta_nomore
- ucmdr \
-{Commands currently understood:
-help get this list of commands
-seen <nick> ask after someone (I'll tell them you asked)
-summon <username> invite a logged-on user onto IRC} {}
+ upvar #0 help_topics([irctolower [string trim $text]]) info
+ if {![info exists info]} { ucmdr "No help on $text, sorry." {} }
+ ucmdr $info {}
+}
+
+def_ucmd ? {
+ global help_topics
+ ucmdr $help_topics() {}
}
proc manyset {list args} {
}
}
-def_ucmd summon {
- set target [ta_word]
- ta_nomore
+proc check_username {target} {
if {
[string length $target] > 8 ||
[regexp {[^-0-9a-z]} $target] ||
![regexp {^[a-z]} $target]
} { error "invalid username" }
+}
+
+proc nickdb__head {} {
+ uplevel 1 {
+ set nl [irctolower $n]
+ upvar #0 nickdb($nl) ndbe
+ binary scan $nl H* nh
+ set nfn users/$nh
+ if {![info exists ndbe] && [file exists $nfn]} {
+ set f [file open $nfn r]
+ try_except_finally { set newval [read $f] } {} { close $f }
+ if {[llength $newval] % 2} { error "invalid length" }
+ set ndbe $newval
+ }
+ }
+}
+
+proc def_nickdb {name arglist body} {
+ proc nickdb_$name $arglist "nickdb__head; $body"
+}
+
+def_nickdb exists {n} {
+ return [info exists ndbe]
+}
+
+def_nickdb delete {n} {
+ catch { unset ndbe }
+ file delete $nfn
+}
+
+set default_settings {timeformat ks}
+
+def_nickdb set {n args} {
+ global default_settings
+ if {![info exists ndbe]} { set ndbe $default_settings }
+ foreach {key value} [concat $ndbe $args] { set a($key) $value }
+ set newval {}
+ foreach {key value} [array get a] { lappend newval $key $value }
+ set f [open $nfn.new w]
+ try_except_finally {
+ puts $f $newval
+ close $f
+ file rename -force $nfn.new $nfn
+ } {
+ catch { close $f }
+ } {
+ }
+ set ndbe $newval
+}
+
+def_nickdb opt {n key} {
+ global default_settings
+ if {[info exists ndbe]} {
+ set l $ndbe
+ } else {
+ set l $default_settings
+ }
+ foreach {tkey value} $l {
+ if {"$tkey" == "$key"} { return $value }
+ }
+ error "unset setting $key"
+}
+
+proc check_notonchan {} {
+ upvar 1 dest dest
+ if {[ischan $dest]} { error "that command must be sent privately" }
+}
+
+proc nick_securitycheck {strict} {
+ upvar 1 n n
+ if {![nickdb_exists $n]} { error "you are unknown to me, use `register'." }
+ set wantu [nickdb_opt $n username]
+ if {![string length $wantu]} {
+ if {$strict} {
+ error "that feature is only available to secure users, sorry."
+ } else {
+ return
+ }
+ }
+ upvar #0 nick_username($n) nu
+ if {![info exists nu]} {
+ error "nick $n is secure, you must identify yourself first."
+ }
+ if {"$wantu" != "$nu"} {
+ error "you are the wrong user - the nick $n belongs to $wantu, not $nu"
+ }
+}
+
+def_ucmd register {
+ prefix_nick
+ check_notonchan
+ set old [nickdb_exists $n]
+ if {$old} { nick_securitycheck 0 }
+ switch -exact [string tolower [string trim $text]] {
+ {} {
+ upvar #0 nick_username($n) nu
+ if {![info exists nu]} {
+ ucmdr {} \
+ "You must identify yourself before using `register'. See `help identify'."
+ }
+ nickdb_set $n username $nu
+ ucmdr {} {} "makes a note of your username." {}
+ }
+ delete {
+ nickdb_delete $n
+ ucmdr {} {} "forgets your nickname." {}
+ }
+ insecure {
+ nickdb_set $n username {}
+ if {$old} {
+ ucmdr {} "Security is now disabled for your nickname !"
+ } else {
+ ucmdr {} "This is fine, but bear in mind that people will be able to mess with your settings. Channel management features need a secure registration." "makes an insecure registration for your nick."
+ }
+ }
+ }
+}
+
+def_ucmd identpass {
+ set username [ta_word]
+ set passmd5 [md5sum [ta_word]]
+ ta_nomore
+ prefix_nick
+ check_isprivmsg
+ upvar #0 nick_onchans($n) onchans
+ if {![info exists onchans] || ![llength $onchans]} {
+ ucmdr "You must be on a channel with me to identify yourself." {}
+ }
+ check_username $username
+ exec userv --timeout 3 $username << "$passmd5\n" > /dev/null \
+ irc-identpass $n
+ upvar #0 nick_username($n) rec_username
+ set rec_username $username
+ ucmdr "Pleased to see you, $username." {}
+}
+
+def_ucmd summon {
+ set target [ta_word]
+ ta_nomore
+ check_username $target
prefix_nick
upvar #0 lastsummon($target) ls
}
}
+proc md5sum {value} { exec md5sum << $value }
+
def_ucmd seen {
global lastseen nick
prefix_nick
#fconfigure $sock -translation binary
fconfigure $sock -translation crlf
- sendout USER guest 0 * "chiark testing bot"
+ sendout USER blight 0 * $ownfullname
sendout NICK $nick
fileevent $sock readable onread
}
+loadhelp
+
#if {![regexp {tclsh} $argv0]} {
# vwait terminate
#}