+proc showintervalsecs {howlong} {
+ if {$howlong < 1000} {
+ return "${howlong}s"
+ } else {
+ if {$howlong < 1000000} {
+ set pfx k
+ set scale 1000
+ } else {
+ set pfx M
+ set scale 1000000
+ }
+ set value [expr "$howlong.0 / $scale"]
+ foreach {min format} {100 %.0f 10 %.1f 1 %.2f} {
+ if {$value < $min} continue
+ return [format "$format${pfx}s" $value]
+ }
+ }
+}
+
+proc showinterval {howlong} {
+ if {$howlong <= 0} {
+ return {just now}
+ } else {
+ return "[showintervalsecs $howlong] ago"
+ }
+}
+
+proc showtime {when} {
+ return [showinterval [expr {[clock seconds] - $when}]]
+}
+
+proc def_msgproc {name argl body} {
+ proc msg_$name "varbase $argl" "\
+ upvar #0 msg/\$varbase/dest d\n\
+ upvar #0 msg/\$varbase/str s\n\
+ upvar #0 msg/\$varbase/accum a\n\
+$body"
+}
+
+def_msgproc begin {dest str} {
+ set d $dest
+ set s $str
+ set a {}
+}
+
+def_msgproc append {str} {
+ set ns "$s$str"
+ if {[string length $s] && [string length $ns] > 65} {
+ msg__sendout $varbase
+ set s " [string trimleft $str]"
+ } else {
+ set s $ns
+ }
+}
+
+def_msgproc finish {} {
+ msg__sendout $varbase
+ unset s
+ unset d
+ return $a
+}
+
+def_msgproc _sendout {} {
+ lappend a [string trimright $s]
+ set s {}
+}
+
+proc looking_whenwhere {when where} {
+ set str [showtime [expr {$when-1}]]
+ if {[string length $where]} { append str " on $where" }
+ return $str
+}
+
+proc recordlastseen_n {n how here} {
+ global lastseen lookedfor
+ set lastseen([irctolower $n]) [list $n [clock seconds] $how]
+ if {!$here} return
+ upvar #0 lookedfor([irctolower $n]) lf
+ if {[info exists lf]} {
+ switch -exact [llength $lf] {
+ 0 {
+ set ml {}
+ }
+ 1 {
+ manyset [lindex $lf 0] when who where
+ set ml [list \
+ "FYI, $who was looking for you [looking_whenwhere $when $where]."]
+ }
+ default {
+ msg_begin tosend $n "FYI, people have been looking for you:"
+ set i 0
+ set fin ""
+ foreach e $lf {
+ incr i
+ if {$i == 1} {
+ msg_append tosend " "
+ } elseif {$i == [llength $lf]} {
+ msg_append tosend " and "
+ set fin .
+ } else {
+ msg_append tosend ", "
+ }
+ manyset $e when who where
+ msg_append tosend \
+ "$who ([looking_whenwhere $when $where])$fin"
+ }
+ set ml [msg_finish tosend]
+ }
+ }
+ unset lf
+ msendprivmsg_delayed 1000 $n $ml
+ }
+}
+
+proc recordlastseen_p {p how here} {
+ prefix_nick
+ recordlastseen_n $n $how $here
+}
+
+proc chanmode_arg {} {
+ upvar 2 args cm_args
+ set rv [lindex $cm_args 0]
+ set cm_args [lreplace cm_args 0 0]
+ return $rv
+}
+
+proc chanmode_o1 {m g p chan} {
+ global nick
+ prefix_nick
+ set who [chanmode_arg]
+ recordlastseen_n $n "being nice to $who" 1
+ if {"[irctolower $who]" == "[irctolower $nick]"} {
+ sendprivmsg $n Thanks.
+ }
+}
+
+proc chanmode_o0 {m g p chan} {
+ global nick chandeop
+ prefix_nick
+ set who [chanmode_arg]
+ recordlastseen_p $p "being mean to $who" 1
+ if {"[irctolower $who]" == "[irctolower $nick]"} {
+ set chandeop($chan) [list [clock seconds] $p]
+ }
+}
+
+proc msg_MODE {p c dest modelist args} {
+ if {![ischan $dest]} return
+ if {[regexp {^\-(.+)$} $modelist dummy modelist]} {
+ set give 0
+ } elseif {[regexp {^\+(.+)$} $modelist dummy modelist]} {
+ set give 1
+ } else {
+ error "invalid modelist"
+ }
+ foreach m [split $modelist] {
+ set procname chanmode_$m$give
+ if {[catch { info body $procname }]} {
+ recordlastseen_p $p "fiddling with $dest" 1
+ } else {
+ $procname $m $give $p $dest
+ }
+ }
+}
+
+proc msg_NICK {p c newnick} {
+ prefix_nick
+ recordlastseen_n $n "changing nicks to $newnick" 0
+ recordlastseen_n $newnick "changing nicks from $n" 1
+}
+
+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 }
+