9 proc privmsg_unlogged {prefix ischan params} {
11 [regexp {^![a-z][-a-z]*[a-z]( .*)?$} [lindex $params 1]]} {
14 # on-channel message, ignore
15 set chan [lindex $params 0]
16 upvar #0 chan_lastactivity([irctolower $chan]) la
17 set la [clock seconds]
18 catch { recordlastseen_p $prefix "talking on $chan" 1 }
22 proc showintervalsecs {howlong abbrev} {
23 return [showintervalsecs/[opt timeformat] $howlong $abbrev]
26 proc showintervalsecs/ks {howlong abbrev} {
27 if {$howlong < 1000} {
30 if {$howlong < 1000000} {
37 set value [expr "$howlong.0 / $scale"]
38 foreach {min format} {100 %.0f 10 %.1f 1 %.2f} {
39 if {$value < $min} continue
40 return [format "$format${pfx}s" $value]
45 proc format_qty {qty unit abbrev} {
48 append o [string range $unit 0 0]
52 if {$qty != 1} { append o s }
57 proc showintervalsecs/hms {qty abbrev} {
58 set ul {second 60 minute 60 hour 24 day 7 week}
60 while {[llength $ul] > 1 && $qty >= [set uv [lindex $ul 1]]} {
61 set remainu [lindex $ul 0]
62 set remainv [expr {$qty % $uv}]
63 set qty [expr {($qty-$remainv)/$uv}]
64 set ul [lreplace $ul 0 1]
66 set o [format_qty $qty [lindex $ul 0] $abbrev]
68 if {!$abbrev} { append o " " }
69 append o [format_qty $remainv $remainu $abbrev]
74 proc showinterval {howlong} {
78 return "[showintervalsecs $howlong 0] ago"
82 proc showtime {when} {
83 return [showinterval [expr {[clock seconds] - $when}]]
86 proc parse_interval {specified min} {
87 if {![regexp {^([0-9]+)([a-z]+)$} $specified dummy value unit]} {
88 error "invalid syntax for interval"
95 default { error "unknown unit of time $unit" }
97 if {$value > 86400*21/$u} { error "interval too large" }
98 set result [expr {$value*$u}]
99 if {$result < $min} { error "interval too small (<${min}s)" }
103 proc def_msgproc {name argl body} {
104 proc msg_$name "varbase $argl" "\
105 upvar #0 msg/\$varbase/dest d\n\
106 upvar #0 msg/\$varbase/str s\n\
107 upvar #0 msg/\$varbase/accum a\n\
111 def_msgproc begin {dest str} {
117 def_msgproc append {str} {
119 if {[string length $s] && [string length $ns] > 65} {
120 msg__sendout $varbase
121 set s " [string trimleft $str]"
127 def_msgproc finish {} {
128 msg__sendout $varbase
134 def_msgproc _sendout {} {
135 lappend a [string trimright $s]
139 proc looking_whenwhere {when where} {
140 set str [showtime [expr {$when-1}]]
141 if {[string length $where]} { append str " on $where" }
145 proc recordlastseen_n {n how here} {
146 global lastseen lookedfor
147 set lastseen([irctolower $n]) [list $n [clock seconds] $how]
149 upvar #0 lookedfor([irctolower $n]) lf
150 if {[info exists lf]} {
151 switch -exact [llength $lf] {
156 manyset [lindex $lf 0] when who where
158 "FYI, $who was looking for you [looking_whenwhere $when $where]."]
161 msg_begin tosend $n "FYI, people have been looking for you:"
167 msg_append tosend " "
168 } elseif {$i == [llength $lf]} {
169 msg_append tosend " and "
172 msg_append tosend ", "
174 manyset $e when who where
176 "$who ([looking_whenwhere $when $where])$fin"
178 set ml [msg_finish tosend]
182 msendprivmsg_delayed 1000 $n $ml
186 proc note_topic {showoff whoby topic} {
187 set msg "FYI, $whoby has changed the topic on $showoff"
188 if {[string length $topic] < 160} {
189 append msg " to $topic"
191 append msg " but it is too long to reproduce here !"
193 set showoff [irctolower $showoff]
194 set tell [chandb_get $showoff topictell]
195 if {[lsearch -exact $tell *] >= 0} {
196 set tryspies [chandb_list]
200 foreach spy $tryspies {
201 set see [chandb_get $spy topicsee]
202 if {[lsearch -exact $see $showoff] >= 0 || \
203 ([lsearch -exact $see *] >= 0 && \
204 [lsearch -exact $tell $spy] >= 0)} {
205 sendprivmsg $spy $msg
210 proc recordlastseen_p {p how here} {
212 recordlastseen_n $n $how $here
215 proc chanmode_arg {} {
217 set rv [lindex $cm_args 0]
218 set cm_args [lreplace cm_args 0 0]
222 proc chanmode_o1 {m g p chan} {
223 global nick chan_initialop
225 set who [chanmode_arg]
226 recordlastseen_n $n "being nice to $who" 1
227 if {"[irctolower $who]" == "[irctolower $nick]"} {
228 set nlower [irctolower $n]
229 upvar #0 nick_unique($nlower) u
230 if {[chandb_exists $chan]} {
231 sendprivmsg $n Thanks.
232 } elseif {![info exists u]} {
233 sendprivmsg $n {Op me while not on the channel, why don't you ?}
235 set chan_initialop([irctolower $chan]) $u
237 "Thanks. You can use `channel manager ...' to register this channel."
238 if {![nickdb_exists $n] || ![string length [nickdb_get $n username]]} {
240 "(But to do that you must register your nick securely first.)"
246 proc chanmode_o0 {m g p chan} {
249 set who [chanmode_arg]
250 recordlastseen_p $p "being mean to $who" 1
251 if {"[irctolower $who]" == "[irctolower $nick]"} {
252 set chandeop($chan) [list [clock seconds] $p]
256 proc msg_MODE {p c dest modelist args} {
257 if {![ischan $dest]} return
258 if {[regexp {^\-(.+)$} $modelist dummy modelist]} {
260 } elseif {[regexp {^\+(.+)$} $modelist dummy modelist]} {
263 error "invalid modelist"
265 foreach m [split $modelist] {
266 set procname chanmode_$m$give
267 if {[catch { info body $procname }]} {
268 recordlastseen_p $p "fiddling with $dest" 1
270 $procname $m $give $p $dest
275 proc leaving {lchan} {
276 foreach luser [array names nick_onchans] {
277 upvar #0 nick_onchans($luser) oc
278 set oc [grep tc {"$tc" != "$lchan"} $oc]
280 upvar #0 chan_nicks($lchan) nlist
282 upvar #0 chan_lastactivity($lchan) la
286 proc doleave {lchan} {
291 proc dojoin {lchan} {
294 set chan_nicks($lchan) {}
297 proc check_justme {lchan} {
299 upvar #0 chan_nicks($lchan) nlist
300 if {[llength $nlist] != 1} return
301 if {"[lindex $nlist 0]" != "[irctolower $nick]"} return
302 if {[chandb_exists $lchan]} {
303 set mode [chandb_get $lchan mode]
304 if {"$mode" != "*"} {
305 sendout MODE $lchan $mode
307 set topic [chandb_get $lchan topicset]
308 if {[string length $topic]} {
309 sendout TOPIC $lchan $topic
316 proc process_kickpart {chan user} {
319 set luser [irctolower $user]
320 set lchan [irctolower $chan]
321 if {![ischan $chan]} { error "not a channel" }
322 if {"$luser" == "[irctolower $nick]"} {
325 upvar #0 nick_onchans($luser) oc
326 upvar #0 chan_nicks($lchan) nlist
327 set oc [grep tc {"$tc" != "$lchan"} $oc]
328 set nlist [grep tn {"$tn" != "$luser"} $nlist]
330 if {![llength $oc]} {
338 proc msg_TOPIC {p c dest topic} {
340 if {![ischan $dest]} return
341 recordlastseen_n $n "changing the topic on $dest" 1
342 note_topic [irctolower $dest] $n $topic
345 proc msg_KICK {p c chans users comment} {
346 set chans [split $chans ,]
347 set users [split $users ,]
348 if {[llength $chans] > 1} {
349 foreach chan $chans user $users { process_kickpart $chan $user }
351 foreach user $users { process_kickpart [lindex $chans 0] $user }
355 proc msg_KILL {p c user why} {
360 set nick_arys {onchans username unique}
361 # nick_onchans($luser) -> [list ... $lchan ...]
362 # nick_username($luser) -> <securely known local username>
363 # nick_unique($luser) -> <counter>
364 # nick_case($luser) -> $user (valid even if no longer visible)
365 # nick_markid($luser) -> <after id for marktime>
367 # chan_nicks($lchan) -> [list ... $luser ...]
368 # chan_lastactivity($lchan) -> [clock seconds]
370 proc lnick_forget {luser} {
371 global nick_arys chan_nicks
372 lnick_marktime_cancel $luser
373 foreach ary $nick_arys {
374 upvar #0 nick_${ary}($luser) av
377 foreach lch [array names chan_nicks] {
378 upvar #0 chan_nicks($lch) nlist
379 set nlist [grep tn {"$tn" != "$luser"} $nlist]
384 proc nick_forget {user} {
385 global nick_arys chan_nicks
386 lnick_forget [irctolower $user]
390 proc nick_case {user} {
392 set nick_case([irctolower $user]) $user
395 proc msg_NICK {p c newnick} {
396 global nick_arys nick_case calling_nick
398 recordlastseen_n $n "changing nicks to $newnick" 0
399 set calling_nick $newnick
400 recordlastseen_n $newnick "changing nicks from $n" 1
401 set luser [irctolower $n]
402 lnick_marktime_cancel $luser
403 set lusernew [irctolower $newnick]
404 foreach ary $nick_arys {
405 upvar #0 nick_${ary}($luser) old
406 upvar #0 nick_${ary}($lusernew) new
407 if {[info exists new]} { error "nick collision ?! $ary $n $newnick" }
408 if {[info exists old]} { set new $old; unset old }
410 upvar #0 nick_onchans($lusernew) oc
412 upvar #0 chan_nicks($ch) nlist
413 set nlist [grep tn {"$tn" != "$luser"} $nlist]
414 lappend nlist $lusernew
416 lnick_marktime_start $lusernew "Hi." 500
420 proc nick_ishere {n} {
422 upvar #0 nick_unique([irctolower $n]) u
423 if {![info exists u]} { set u [incr nick_counter].$n.[clock seconds] }
427 proc msg_JOIN {p c chan} {
429 recordlastseen_n $n "joining $chan" 1
430 set nl [irctolower $n]
431 set lchan [irctolower $chan]
432 upvar #0 nick_onchans($nl) oc
433 upvar #0 chan_nicks($lchan) nlist
434 if {![info exists oc]} {
435 global marktime_join_startdelay
436 lnick_marktime_start $nl "Welcome." $marktime_join_startdelay
442 proc msg_PART {p c chan args} {
444 set msg "leaving $chan"
445 if {[llength $args]} {
446 set why [lindex $args 0]
447 if {"[irctolower $why]" != "[irctolower $n]"} { append msg " ($why)" }
449 recordlastseen_n $n $msg 1
450 process_kickpart $chan $n
452 proc msg_QUIT {p c why} {
454 recordlastseen_n $n "leaving ($why)" 0
458 proc msg_PRIVMSG {p c dest text} {
462 if {[ischan $dest]} {
463 recordlastseen_n $n "invoking me in $dest" 1
466 recordlastseen_n $n "talking to me" 1
471 execute_usercommand $p $c $n $output $dest $text
474 proc msg_INVITE {p c n chan} {
475 after 1000 [list dojoin [irctolower $chan]]
478 proc grep {var predicate list} {
482 if {[uplevel 1 [list expr $predicate]]} { lappend o $v }
487 proc msg_353 {p c dest type chan nicklist} {
488 global names_chans nick_onchans
489 set lchan [irctolower $chan]
490 upvar #0 chan_nicks($lchan) nlist
491 lappend names_chans $lchan
492 if {![info exists nlist]} {
493 # We don't think we're on this channel, so ignore it !
494 # Unfortunately, because we don't get a reply to PART,
495 # we have to remember ourselves whether we're on a channel,
496 # and ignore stuff if we're not, to avoid races. Feh.
500 foreach user [split $nicklist { }] {
501 regsub {^[@+]} $user {} user
502 if {![string length $user]} continue
504 set luser [irctolower $user]
505 upvar #0 nick_onchans($luser) oc
507 lappend nlist_new $luser
513 proc msg_366 {p c args} {
514 global names_chans nick_onchans
515 set lchan [irctolower $c]
516 foreach luser [array names nick_onchans] {
517 upvar #0 nick_onchans($luser) oc
518 if {[llength names_chans] > 1} {
519 set oc [grep tc {[lsearch -exact $tc $names_chans] >= 0} $oc]
521 if {![llength $oc]} { lnick_forget $n }
526 proc check_username {target} {
528 [string length $target] > 8 ||
529 [regexp {[^-0-9a-z]} $target] ||
530 ![regexp {^[a-z]} $target]
531 } { error "invalid username" }
534 proc somedb__head {} {
536 set idl [irctolower $id]
537 upvar #0 ${nickchan}db($idl) ndbe
538 binary scan $idl H* idh
539 set idfn $fprefix$idh
540 if {![info exists iddbe] && [file exists $idfn]} {
542 try_except_finally { set newval [read $f] } {} { close $f }
543 if {[llength $newval] % 2} { error "invalid length" }
549 proc def_somedb {name arglist body} {
550 foreach {nickchan fprefix} {
555 proc ${nickchan}db_$name $arglist \
556 "set nickchan $nickchan; set fprefix $fprefix; $body"
562 foreach path [glob -nocomplain -path $fprefix *] {
563 binary scan $path "A[string length $fprefix]A*" afprefix thinghex
564 if {"$afprefix" != "$fprefix"} { error "wrong prefix $path $afprefix" }
565 lappend list [binary format H* $thinghex]
570 proc def_somedb_id {name arglist body} {
571 def_somedb $name [concat id $arglist] "somedb__head; $body"
574 def_somedb_id exists {} {
575 return [info exists iddbe]
578 def_somedb_id delete {} {
579 catch { unset iddbe }
583 set default_settings_nick {
587 tellrel {remind 3600 30}
590 set default_settings_chan {
599 set default_settings_msgs {
603 # inbound -> [<nick> <time_t> <message>] ...
604 # outbound -> [<nick> <time_t(earliest)> <count>] ...
606 def_somedb_id set {args} {
607 upvar #0 default_settings_$nickchan def
608 if {![info exists iddbe]} { set iddbe $def }
609 foreach {key value} [concat $iddbe $args] { set a($key) $value }
611 foreach {key value} [array get a] { lappend newval $key $value }
612 set f [open $idfn.new w]
616 file rename -force $idfn.new $idfn
624 def_somedb_id get {key} {
625 upvar #0 default_settings_$nickchan def
626 if {[info exists iddbe]} {
627 set l [concat $iddbe $def]
631 foreach {tkey value} $l {
632 if {"$tkey" == "$key"} { return $value }
634 error "unset setting $key"
639 if {[info exists calling_nick]} { set n $calling_nick } { set n {} }
640 return [nickdb_get $n $key]
643 proc check_notonchan {} {
645 if {[ischan $dest]} { usererror "That command must be sent privately." }
648 proc nick_securitycheck {strict} {
650 if {![nickdb_exists $n]} {
651 usererror "You are unknown to me, use `register'."
653 set wantu [nickdb_get $n username]
654 if {![string length $wantu]} {
656 usererror "That feature is only available to secure users, sorry."
661 set luser [irctolower $n]
662 upvar #0 nick_username($luser) nu
663 if {![info exists nu]} {
664 usererror "Nick $n is secure, you must identify yourself first."
666 if {"$wantu" != "$nu"} {
667 usererror "You are the wrong user -\
668 the nick $n belongs to $wantu, not $nu."
672 proc channel_ismanager {channel n} {
673 set mgrs [chandb_get $channel managers]
674 return [expr {[lsearch -exact [irctolower $mgrs] [irctolower $n]] >= 0}]
677 proc channel_securitycheck {channel} {
679 if {![channel_ismanager $channel $n]} {
680 usererror "You are not a manager of $channel."
685 proc def_chancmd {name body} {
686 proc channel/$name {} \
687 " upvar 1 target chan; upvar 1 n n; upvar 1 text text; $body"
690 proc ta_listop {findnow procvalue} {
691 # findnow and procvalue are code fragments which will be executed
692 # in the caller's level. findnow should set ta_listop_ev to
693 # the current list, and procvalue should treat ta_listop_ev as
694 # a proposed value in the list and check and possibly modify
695 # (canonicalise?) it. After ta_listop, ta_listop_ev will
696 # be the new value of the list.
697 upvar 1 ta_listop_ev exchg
700 switch -exact _$opcode {
704 foreach item $exchg { set array($item) 1 }
707 error "list change opcode must be one of + - ="
710 foreach exchg [split $text " "] {
711 if {![string length $exchg]} continue
713 if {"$opcode" != "-"} {
716 catch { unset array($exchg) }
719 set exchg [lsort [array names array]]
722 def_chancmd manager {
724 if {[chandb_exists $chan]} {
725 set ta_listop_ev [chandb_get $chan managers]
727 set ta_listop_ev [list [irctolower $n]]
730 check_nick $ta_listop_ev
731 set ta_listop_ev [irctolower $ta_listop_ev]
733 if {[llength $ta_listop_ev]} {
734 chandb_set $chan managers $ta_listop_ev
735 ucmdr "Managers of $chan: $ta_listop_ev" {}
738 ucmdr {} {} "forgets about managing $chan." {}
742 def_chancmd autojoin {
744 switch -exact [string tolower $yesno] {
747 default { error "channel autojoin must be `yes' or `no' }
749 chandb_set $chan autojoin $nv
750 ucmdr [expr {$nv ? "I will join $chan when I'm restarted " : \
751 "I won't join $chan when I'm restarted "}] {}
754 def_chancmd userinvite {
755 set nv [string tolower [ta_word]]
757 pub { set txt "!invite will work for $chan, but it won't work by /msg" }
758 here { set txt "!invite and /msg invite will work, but only for users who are already on $chan." }
759 all { set txt "Any user will be able to invite themselves or anyone else to $chan." }
760 none { set txt "I will not invite anyone to $chan." }
762 error "channel userinvite must be `pub', `here', `all' or `none'
765 chandb_set $chan userinvite $nv
771 switch -exact $what {
774 chandb_set $chan topicset {}
775 ucmdr "I won't ever change the topic of $chan." {}
778 set t [string trim $text]
779 if {![string length $t]} {
780 error "you must specific the topic to set"
782 chandb_set $chan topicset $t
783 ucmdr "Whenever I'm alone on $chan, I'll set the topic to $t." {}
787 set ta_listop_ev [chandb_get $chan topic$what]
789 if {"$ta_listop_ev" != "*"} {
790 if {![ischan $ta_listop_ev]} {
791 error "bad channel \`$ta_listop_ev' in topic $what"
793 set ta_listop_ev [irctolower $ta_listop_ev]
796 chandb_set $chan topic$what $ta_listop_ev
797 ucmdr "Topic $what list for $chan: $ta_listop_ev" {}
800 usererror "Unknown channel topic subcommand - see help channel."
807 if {"$mode" != "*" && ![regexp {^(([-+][imnpst]+)+)$} $mode mode]} {
808 error {channel mode must be * or match ([-+][imnpst]+)+}
810 chandb_set $chan mode $mode
811 if {"$mode" == "*"} {
812 ucmdr "I won't ever change the mode of $chan." {}
814 ucmdr "Whenever I'm alone on $chan, I'll set the mode to $mode." {}
819 if {[chandb_exists $chan]} {
820 set l "Settings for $chan: autojoin "
821 append l [lindex {no yes} [chandb_get $chan autojoin]]
822 append l ", mode " [chandb_get $chan mode]
823 append l ", userinvite " [chandb_get $chan userinvite] "."
824 append l "\nManagers: "
825 append l [join [chandb_get $chan managers] " "]
826 foreach {ts sep} {see "\n" tell " "} {
827 set t [chandb_get $chan topic$ts]
830 append l "Topic $ts list: $t."
832 append l "Topic $ts list is empty."
836 set t [chandb_get $chan topicset]
837 if {[string length $t]} {
838 append l "Topic to set: $t"
840 append l "I will not change the topic."
844 ucmdr {} "The channel $chan is not managed."
848 proc channelmgr_monoop {} {
853 upvar 1 target target
858 if {[ischan $dest]} { set target $dest }
859 if {[ta_anymore]} { set target [ta_word] }
861 if {![info exists target]} {
862 usererror "You must specify, or invoke me on, the relevant channel."
864 if {![info exists chan_nicks([irctolower $target])]} {
865 usererror "I am not on $target."
867 if {![ischan $target]} { error "not a valid channel" }
869 if {![chandb_exists $target]} {
870 usererror "$target is not a managed channel."
872 channel_securitycheck $target
877 sendout MODE $target +o $n
886 global chan_nicks errorCode errorInfo
889 if {[ischan $dest]} {
896 set ltarget [irctolower $target]
897 if {![ischan $target]} { error "$target is not a channel" }
898 if {![info exists chan_nicks($ltarget)]} {
899 usererror "I am not on $target."
901 set ui [chandb_get $ltarget userinvite]
903 if {"$ui" == "pub" && !$onchan} {
904 usererror "Invitations to $target must be made there with !invite."
906 if {"$ui" != "all"} {
907 if {[lsearch -exact $chan_nicks($ltarget) [irctolower $n]] < 0} {
908 usererror "Invitations to $target may only be made\
909 by a user on the channel."
912 if {"$ui" == "none"} {
913 usererror "Sorry, I've not been authorised\
914 to invite people to $target."
917 if {"$errorCode" == "BLIGHT USER" && [channel_ismanager $target $n]} {
921 if {"$errorCode" == "BLIGHT USER"} {
922 usererror "$emsg2 Therefore you can't use your\
923 channel manager privilege. $emsg"
925 error $error $errorInfo $errorCode
929 error $emsg $errorInfo $errorCode
933 usererror "You have to say who to invite."
936 while {[ta_anymore]} {
937 set invitee [ta_word]
939 lappend invitees $invitee
941 foreach invitee $invitees {
942 sendout INVITE $invitee $ltarget
944 set who [lindex $invitees 0]
945 switch -exact llength $invitees {
946 0 { error "zero invitees" }
948 2 { append who " and [lindex $invitees 1]" }
950 set who [join [lreplace $invitees end end] ", "]
951 append who " and [lindex $invitees [llength $invitees]]"
954 ucmdr {} {} {} "invites $who to $target."
958 if {[ischan $dest]} { set target $dest }
964 if {[ischan $subcmd]} {
972 if {![info exists target]} { error "privately, you must specify a channel" }
973 set procname channel/$subcmd
974 if {"$subcmd" != "show"} {
975 if {[catch { info body $procname }]} {
976 usererror "unknown channel setting $subcmd."
979 if {[chandb_exists $target]} {
980 channel_securitycheck $target
983 upvar #0 chan_initialop([irctolower $target]) io
984 upvar #0 nick_unique([irctolower $n]) u
985 if {![info exists io]} {
986 usererror "$target is not a managed channel."
989 usererror "You are not the interim manager of $target."
991 if {"$subcmd" != "manager"} {
992 usererror "Please use `channel manager' first."
1000 global nick_case ownmailaddr ownfullname
1003 set target [ta_word]
1004 if {![string length $text]} { error "tell them what?" }
1006 set ltarget [irctolower $target]
1008 if {[info exists nick_case($ltarget)]} { set ctarget $nick_case($ltarget) }
1010 manyset [nickdb_get $target tellsec] sec mailto mailwhy
1011 switch -exact $sec {
1013 set now [clock seconds]
1014 set inbound [msgsdb_get $ltarget inbound]
1015 lappend inbound $n $now $text
1016 msgsdb_set $ltarget inbound $inbound
1018 set outbound [msgsdb_get $n outbound]
1021 foreach {recip time count} $outbound {
1022 if {"[irctolower $recip]" == "$ltarget"} {
1027 lappend noutbound $recip $time $count
1030 lappend noutbound $ctarget $now 1
1032 msgsdb_set $n outbound $noutbound
1034 ucmdr "OK, I'll tell $ctarget." {}
1036 ucmdr "OK, I'll tell $ctarget that too." {}
1040 set fmtmsg [exec fmt << " $text"]
1041 exec /usr/sbin/sendmail -odb -oi -t -oee -f $mailwhy \
1043 "From: $ownmailaddr ($ownfullname)
1045 Subject: IRC tell from $n
1047 $n asked me[expr {[ischan $dest] ? " on $dest" : ""}] to tell you:
1048 [exec fmt << " $text"]
1050 (This message was for your nick $ctarget; your account $mailwhy
1051 arranged for it to be forwarded to $mailto.)
1054 "I've mailed $ctarget at $mailto, which is what they prefer." \
1058 usererror "Sorry, $ctarget does not want me to take messages."
1061 error "bad tellsec $sec"
1068 set target [ta_word]; ta_nomore
1073 set myself [expr {"$target" != "$n"}]
1075 set ltarget [irctolower $target]
1076 upvar #0 nick_case($ltarget) ctarget
1078 if {[info exists ctarget]} {
1079 upvar #0 nick_onchans($ltarget) oc
1080 upvar #0 nick_username($ltarget) nu
1081 if {[info exists oc]} { set nshow $ctarget }
1083 if {![nickdb_exists $ltarget]} {
1084 set ol "$nshow is not a registered nick."
1085 } elseif {[string length [set username [nickdb_get $target username]]]} {
1086 set ol "The nick $nshow belongs to the user $username."
1088 set ol "The nick $nshow is registered (but not to a username)."
1090 if {![info exists ctarget] || ![info exists oc]} {
1092 append ol "\nI can't see $nshow on anywhere."
1094 append ol "\nYou aren't on any channels with me."
1096 } elseif {![info exists nu]} {
1097 append ol "\n$nshow has not identified themselves."
1098 } elseif {![info exists username]} {
1099 append ol "\n$nshow has identified themselves as the user $nu."
1100 } elseif {"$nu" != "$username"} {
1101 append ol "\nHowever, $nshow is being used by the user $nu."
1103 append ol "\n$nshow has identified themselves to me."
1111 set old [nickdb_exists $n]
1112 if {$old} { nick_securitycheck 0 }
1113 set luser [irctolower $n]
1114 switch -exact [string tolower [string trim $text]] {
1116 upvar #0 nick_username($luser) nu
1117 if {![info exists nu]} {
1119 "You must identify yourself before using `register'. See `help identify', or use `register insecure'."
1121 nickdb_set $n username $nu
1122 ucmdr {} {} "makes a note of your username." {}
1126 ucmdr {} {} "forgets your nickname." {}
1129 nickdb_set $n username {}
1131 ucmdr {} "Security is now disabled for your nickname !"
1133 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."
1137 error "you mean register / register delete / register insecure"
1142 proc timeformat_desc {tf} {
1144 ks { return "Times will be displayed in seconds or kiloseconds." }
1145 hms { return "Times will be displayed in hours, minutes, etc." }
1146 default { error "invalid timeformat: $v" }
1151 proc def_setting {opt show_body set_body} {
1153 lappend settings $opt
1154 proc set_show/$opt {} "
1158 if {![string length $set_body]} return
1159 proc set_set/$opt {} "
1166 proc tellme_sec_desc {v} {
1167 manyset $v sec mailto
1168 switch -exact $sec {
1170 return "I'll tell you your messages whenever I see you."
1174 "I'll keep the bodies of your messages private until you identify yourself."
1177 return "I shan't accept messages for you."
1180 return "I'll forward your messages by email to $mailto."
1183 error "bad tellsec $sec"
1188 proc tellme_rel_desc {v} {
1189 manyset $v rel every within
1190 switch -exact $rel {
1192 return "As soon as I've told you, I'll forget the message - note that this means messages can get lost !"
1198 set u ", or talk on channel within [showintervalsecs $within 1] of me having told you"
1201 error "bad tellrel $rel"
1204 return "I'll remind you every [showintervalsecs $every 1] until you say delmsg$u."
1207 def_setting timeformat {
1208 set tf [nickdb_get $n timeformat]
1209 return "$tf: [timeformat_desc $tf]"
1211 set tf [string tolower [ta_word]]
1213 set desc [timeformat_desc $tf]
1214 nickdb_set $n timeformat $tf
1218 proc marktime_desc {mt} {
1219 if {"$mt" == "off"} {
1220 return "I will not send you periodic messages."
1221 } elseif {"$mt" == "once"} {
1222 return "I will send you one informational message when I see you."
1224 return "I'll send you a message every [showintervalsecs $mt 0]."
1228 def_setting marktime {
1229 set mt [nickdb_get $n marktime]
1231 if {[string match {[0-9]*} $mt]} { append p s }
1233 append p [marktime_desc $mt]
1237 set mt [string tolower [ta_word]]
1240 if {"$mt" == "off" || "$mt" == "once"} {
1242 set mt [parse_interval $mt $marktime_min]
1244 nickdb_set $n marktime $mt
1245 lnick_marktime_start [irctolower $n] "So:" 500
1246 ucmdr {} [marktime_desc $mt]
1249 def_setting security {
1250 set s [nickdb_get $n username]
1251 if {[string length $s]} {
1252 return "Your nick, $n, is controlled by the user $s."
1254 return "Your nick, $n, is not secure."
1258 def_setting tellme {
1259 set secv [nickdb_get $n tellsec]
1260 set ms [tellme_sec_desc $secv]
1262 switch -exact $sec {
1264 set mr [tellme_rel_desc [nickdb_get $n tellrel]]
1272 set setting [string tolower [ta_word]]
1273 switch -exact $setting {
1274 insecure - secure - refuse {
1276 if {"$setting" == "refuse" && [llength [msgsdb_get $n inbound]]} {
1277 usererror "You must delete the messages you have, first."
1283 set u [nickdb_get $n username]
1284 if {![string length $u]} {
1285 usererror "Sorry, you must register secure to have your messages mailed (to prevent the use of this feature for spamming)."
1288 set v [list mailto [ta_word] $u]
1290 unreliable - pester - remind {
1291 manyset [nickdb_get $n tellsec] sec
1292 switch -exact $sec {
1294 error "can't change message delivery conditions when message disposition prevents messages from being left"
1299 if {"$setting" != "unreliable"} {
1300 set every [parse_interval [ta_word] 300]
1303 if {"$setting" == "remind"} {
1305 set within [parse_interval [ta_word] 5]
1309 if {$within > $every} {
1310 error "remind interval must be at least time to respond"
1317 error "invalid tellme setting $setting"
1320 nickdb_set $n tell$sr $v
1321 ucmdr [tellme_${sr}_desc $v] {}
1328 if {![nickdb_exists $n]} {
1329 ucmdr {} "You are unknown to me and so have no settings. (Use `register'.)"
1331 if {![ta_anymore]} {
1333 foreach opt $settings {
1334 lappend ol [format "%-10s %s" $opt [set_show/$opt]]
1336 ucmdr {} [join $ol "\n"]
1339 if {[catch { info body set_show/$opt }]} {
1340 error "no setting $opt"
1342 if {![ta_anymore]} {
1343 ucmdr {} "$opt: [set_show/$opt]"
1345 nick_securitycheck 0
1346 if {[catch { info body set_set/$opt }]} {
1347 error "setting $opt cannot be set with `set'"
1354 def_ucmd identpass {
1355 set username [ta_word]
1356 set passmd5 [md5sum "[ta_word]\n"]
1360 set luser [irctolower $n]
1361 upvar #0 nick_onchans($luser) onchans
1362 if {![info exists onchans] || ![llength $onchans]} {
1363 ucmdr "You must be on a channel with me to identify yourself." {}
1365 check_username $username
1366 exec userv --timeout 3 $username << "$passmd5\n" > /dev/null \
1368 upvar #0 nick_username($luser) rec_username
1369 set rec_username $username
1370 ucmdr "Pleased to see you, $username." {}
1374 set target [ta_word]
1376 check_username $target
1379 upvar #0 lastsummon($target) ls
1380 set now [clock seconds]
1381 if {[info exists ls]} {
1382 set interval [expr {$now - $ls}]
1383 if {$interval < 30} {
1385 "Please be patient; $target was summoned only [showinterval $interval]."
1388 regsub {^[^!]*!} $p {} path
1390 exec userv --timeout 3 $target irc-summon $n $path \
1391 [expr {[ischan $dest] ? "$dest" : ""}] \
1394 regsub -all "\n" $rv { / } rv
1397 if {[regexp {^problem (.*)} $rv dummy problem]} {
1398 ucmdr {} "The user `$target' $problem."
1399 } elseif {[regexp {^ok ([^ ]+) ([0-9]+)$} $rv dummy tty idlesince]} {
1400 set idletime [expr {$now - $idlesince}]
1402 ucmdr {} {} {} "invites $target ($tty[expr {
1403 $idletime > 10 ? ", idle for [showintervalsecs $idletime 0]" : ""
1405 [ischan $dest] ? "join us here" : "talk to you"
1408 error "unexpected response from userv service: $rv"
1412 proc md5sum {value} { exec md5sum << $value }
1415 global lastseen nick
1418 set nlower [irctolower $ncase]
1420 set now [clock seconds]
1421 if {"$nlower" == "[irctolower $nick]"} {
1422 usererror "I am not self-aware."
1423 } elseif {![info exists lastseen($nlower)]} {
1424 set rstr "I've never seen $ncase."
1426 manyset $lastseen($nlower) realnick time what
1427 set howlong [expr {$now - $time}]
1428 set string [showinterval $howlong]
1429 set rstr "I last saw $realnick $string, $what."
1431 if {[ischan $dest]} {
1436 upvar #0 lookedfor($nlower) lf
1437 if {[info exists lf]} { set oldvalue $lf } else { set oldvalue {} }
1438 set lf [list [list $now $n $where]]
1439 foreach v $oldvalue {
1440 if {"[irctolower [lindex $v 1]]" == "[irctolower $n]"} continue
1446 proc lnick_marktime_cancel {luser} {
1447 upvar #0 nick_markid($luser) mi
1448 if {![info exists mi]} return
1449 catch { after cancel $mi }
1453 proc lnick_marktime_doafter {luser why ms} {
1454 lnick_marktime_cancel $luser
1455 upvar #0 nick_markid($luser) mi
1456 set mi [after $ms [list lnick_marktime_now $luser $why]]
1459 proc lnick_marktime_reset {luser} {
1460 set mt [nickdb_get $luser marktime]
1461 if {"$mt" == "off" || "$mt" == "once"} return
1462 lnick_marktime_doafter $luser "Time passes." [expr {$mt*1000}]
1465 proc lnick_marktime_start {luser why ms} {
1466 set mt [nickdb_get $luser marktime]
1467 if {"$mt" == "off"} {
1468 lnick_marktime_cancel $luser
1470 lnick_marktime_doafter $luser $why $ms
1474 proc lnick_marktime_now {luser why} {
1475 upvar #0 nick_onchans($luser) oc
1477 set calling_nick $luser
1478 sendprivmsg $luser [lnick_pingstring $why $oc ""]
1479 lnick_marktime_reset $luser
1482 proc lnick_pingstring {why oc apstring} {
1484 catch { exec uptime } uptime
1485 set nnicks [llength [array names nick_onchans]]
1487 {^ *([0-9:apm]+) +up.*, +(\d+) users?, +load average: +([0-9., ]+) *$} \
1488 $uptime dummy time users load]} {
1489 regsub -all , $load {} load
1490 set uptime "$time $nnicks/$users $load"
1492 append uptime ", $nnicks nicks"
1494 if {[llength $oc]} {
1498 upvar #0 chan_lastactivity($ch) la
1499 if {![info exists la]} continue
1500 if {$la <= $best_la} continue
1501 set since [showintervalsecs [expr {[clock seconds]-$la}] 1]
1502 set activity "$ch $since"
1509 append str " " $uptime " " $activity
1510 if {[string length $apstring]} { append str " " $apstring }
1515 if {[ischan $dest]} {
1516 set oc [irctolower $dest]
1520 set ln [irctolower $n]
1521 if {[info exists nick_onchans($ln)]} {
1522 set oc $nick_onchans($ln)
1526 if {[llength $oc]} { lnick_marktime_reset $ln }
1528 ucmdr {} [lnick_pingstring "Pong!" $oc $text]
1531 proc ensure_globalsecret {} {
1534 if {[info exists globalsecret]} return
1535 set gsfile [open /dev/urandom r]
1536 fconfigure $gsfile -translation binary
1537 set globalsecret [read $gsfile 32]
1538 binary scan $globalsecret H* globalsecret
1544 foreach chan [chandb_list] {
1545 if {[chandb_get $chan autojoin]} { dojoin $chan }