chiark / gitweb /
<lastactivity> is absolute
[ircbot.git] / ledmodule.tcl
index 250f600f9b43f51ccd674e60c9ed032c5027f202..b4a20fd858b890e3eddc1073c7733fe8cc517ec6 100644 (file)
@@ -1,36 +1,5 @@
-# maintains local list of users to userv-slurp config from
-# each user provides list of
-#    monitors
-#    devicesets
-#
-# a monitor specifies
-#    name
-#    IRC channel(s)
-#    nicks ignore totally
-#    nicks ignore presence
-#    nicks prefer speech
-#    time for `a while ago'
-#    time for `very-recently'
-# syntax
-#    nick ignore|nopresence|prefer <glob-pattern> [...]
-#    times <very-recently> <a-while-ago>                  (default 120 450)
-#      (affect subsequent `monitor' directives)
-#    monitor <monname> <#chan>[,<#chan>...]
-#      <monname> must start with <username>:
-#
-# a deviceset specifies
-#    monitor
-#    led-group
-#    led states
-# syntax
-#    leds <led-group> <monname> <state>=<value>
-# where state is one of
-#    [pref]talk[now]  any non-ignored (with `pref', only any preferred)
-#                     nick(s) spoke at least somewhat recently
-#                     (with `now', only if they spoke very recently)
-#    present          at least some non-nopresence nicks present
-#    default          always matches
-# where the first matching state wins; if none, no LEDs are set
+# see ledbot.html
+# $Id$
 
 set helpfile ledhelp
 
@@ -41,7 +10,7 @@ source userv.tcl
 
 defset errchan #$nick
 defset retry_after 900000
-defset chan_after 3000
+defset chan_after 1500
 defset chans_retry 3600000
 defset debug_reset_after 86400000
 
@@ -80,8 +49,8 @@ proc ldebug {facil m} {
     #    d$deviceset
     #    c$lchan
     #    {}             for system stuff
-    if {![llength debugusers]} return
-    if {[regexp {[md]([^:]+)\:} $facil dummy username] &&
+    if {![llength $debugusers]} return
+    if {[regexp {[mdu]([^:]+)\:} $facil dummy username] &&
         [lsearch -exact $debugusers $username]==-1} return
 
     regsub {^(.)} $facil {\1 } cc
@@ -138,8 +107,17 @@ proc mon_nick_is {globlist ln} {
 }
 
 proc_mon gotchanlist {ch nll} {
+    global nick
     if {[lsearch -exact $mm(chans) $ch] == -1} return
-    set mm(present-$ch) $nll
+    set l {}
+    foreach nl $nll {
+       if {![string compare $nl [irctolower $nick]]} continue
+       if {[mon_nick_is $mm(nopresence) $nl]} continue
+       if {[mon_nick_is $mm(ignore) $nl]} continue
+       lappend l $nl
+    }
+    ldebug m$m "$ch names: $l"
+    set mm(present-$ch) $l
     mon_updateall $m
 }
 
@@ -201,10 +179,16 @@ proc_mon destroy {} {
 proc proc_dset {name argl body} {
     proc dset_$name [concat d $argl] "
     upvar #0 deviceset/\$d dd
-    if {\[catch {
+    set returncode \[catch {
         $body
-    } emsg\]==1} {
+    } emsg\]
+    global errorInfo errorCode
+    if {\$returncode==1} {
        reporterr \"error on \$d: \$emsg\"
+    } elseif {\$returncode==2} {
+       return \$emsg
+    } else {
+       return -code \$returncode -errorinfo \$errorInfo -errorcode \$errorCode
     }"
 }
 
@@ -217,6 +201,7 @@ proc_dset setbystate {s} {
        if {![string match *$sq* $s]} continue
        set lv $v; break
     }
+    if {![info exists dd(ichan)]} return
     if {![info exists lv]} {
        reporterr "no state for $d matching$s"
        return
@@ -284,19 +269,24 @@ proc reloaduser {username} {
        set cc(time-recentnow) 120
        set cc(time-recent) 450
        set lno 0
+       set contin {}
        foreach l [split $cfg "\n"] {
            incr lno
-           set l [string trim $l]
+           append contin [string trim $l]
+           if {[regsub {\\$} $contin { } contin]} continue
+           set l $contin
+           set contin {}
            if {[regexp {^\#} $l]} {
-           } elseif {[regexp {^nick\s+(ignore|nopresence|prefer)\s+(\S.*)$} \
-                   $l dummy kind globs]} {
+           } elseif {![regexp {\S} $l]} {
+           } elseif {[regexp {^nick\s+(ignore|nopresence|prefer)\s+(.*)$} \
+                   "$l " dummy kind globs]} {
                set cc($kind) {}
                foreach gl [split $globs " "] {
                    if {![string length $gl]} continue
                    string match $gl {}
                    lappend cc($kind) $gl
                }
-           } elseif {[regexp {^times\s+(\d+)\s+(\d+)$} $l dummy r rnow]} {
+           } elseif {[regexp {^times\s+(\d+)\s+(\d+)$} $l dummy rnow r]} {
                foreach cv {{} now} { set cc(time-recent$cv) [set r$cv] }
            } elseif {[regexp {^monitor\s+(\S+)\s+(\S.*)$} $l dummy m cl]} {
                set cc(chans) {}
@@ -321,7 +311,7 @@ proc reloaduser {username} {
                }
                ldebug m$m "created"
            } elseif {[regexp \
- {^leds\s+([0-9A-Za-z][-:/0-9A-Za-z]+)\s+(\S+)\s+(\S+.*)$} \
+ {^leds\s+([0-9A-Za-z][-.:/0-9A-Za-z]+)\s+(\S+)\s+(\S+.*)$} \
                    $l dummy g m states]} {
                set d $username:$lno:$g
                set sl {}
@@ -343,8 +333,13 @@ proc reloaduser {username} {
                set dd(username) $username
                dset_start $d
                ldebug d$d "created"
+           } else {
+               error "invalid directive or syntax"
            }
        }
+       if {[string length $contin]} {
+           error "continuation line at end of file"
+       }
     } emsg]} {
        reporterr "setup error $username:$lno:$emsg"
        return ""
@@ -377,9 +372,9 @@ proc_dset start {} {
        set ochan [open |$cmdl r]
        fconfigure $ichan -blocking 0 -buffering line
        fconfigure $ochan -blocking 0 -buffering line
-       fileevent $ochan readable [list dset_rledout $d]
        set dd(ichan) $ichan
        set dd(ochan) $ochan
+       fileevent $ochan readable [list dset_rledout $d]
     } emsg]} {
        reporterr "remoteleds startup $d: $emsg"
        catch { close $ichan }
@@ -391,7 +386,7 @@ proc_dset start {} {
 proc_dset rledout {} {
     global errchan
     while {[gets $dd(ochan) l] != -1} {
-       reporterr "remoteleds on $d: $dd(values): $l"
+       reporterr "on $d: $dd(values): $l"
     }
     if {[fblocked $dd(ochan)]} return
     timed_log ">\$$d failure";
@@ -399,7 +394,7 @@ proc_dset rledout {} {
     catch { close $dd(ochan) }
     unset dd(ichan)
     unset dd(ochan)
-    reporterr "remoteleds on $d died"
+    reporterr "on $d died"
     dset_trylater $d
 }
 
@@ -437,8 +432,14 @@ proc config_change {} {
     set config_retry_after [after $chans_retry config_change]
 }
 
+proc allchans_shortly {} {
+    global onchans
+    foreach ch [array names onchans] { chan_shortly $ch }
+}
+
 proc chan_shortly {ch} {
     global chan_after
+    set ch [irctolower $ch]
     upvar #0 onchans($ch) oc
     if {[info exists oc]} {
        manyset $oc status after
@@ -454,7 +455,8 @@ proc chan_shortly {ch} {
 proc msg_353 {p c dest type chan nicklist} {
     set lchan [irctolower $chan]
     set nll [irctolower $nicklist]
-    ldebug c$lchan "got names $nll"
+    regsub -all {[=@*]} $nll {} nll
+    ldebug c$lchan "all names: $nll"
     foreach m [list_objs monitor] {
        mon_gotchanlist $m $lchan $nll
     }
@@ -495,6 +497,7 @@ def_ucmd debug {
 
 def_ucmd nodebug {
     prefix_nick
+    ta_nomore
     global debugusers debug_cancelling
     set debugusers {}
     catch { after cancel $debug_cancelling }
@@ -502,6 +505,13 @@ def_ucmd nodebug {
     reporterr "debug disabled by $n"
 }
 
+proc_dset visibledest {} {
+    regsub {\:[^:]*/} $d/ { } p
+    regsub {^([^:]+)\:\d+\:} $p {\1, } p
+    regsub { $} $p {} p
+    return $p
+}
+
 def_ucmd who {
     set r {}
     foreach m [list_objs monitor] {
@@ -510,8 +520,10 @@ def_ucmd who {
     }
     foreach d [list_objs deviceset] {
        upvar #0 deviceset/$d dd
-       regexp {^[^:]*\:[^:]*} $dd(group) dest
-       lappend r "sending $dd(monname) to $dest"
+       set m $dd(monname)
+       upvar #0 monitor/$m mm
+       if {![info exists mm(chans)]} continue
+       lappend r "sending $m to [dset_visibledest $d]"
     }
     ucmdr [join $r "\n"] {}
 }
@@ -525,8 +537,41 @@ proc connected {} {
     config_change
 }
 
-# JOIN PART
-# KICK KILL QUIT
+proc warn_pref {n} {
+    set nl [irctolower $n]
+    set l {}
+    foreach d [list_objs deviceset] {
+       upvar #0 deviceset/$d dd
+       set m $dd(monname)
+       upvar #0 monitor/$m mm
+       if {![info exists mm(prefer)]} continue
+       if {![mon_nick_is $mm(prefer) $nl]} continue
+       foreach ch $mm(chans) { set wch($ch) 1 }
+       lappend l [dset_visibledest $d]
+    }
+    if {[llength $l]} {
+       sendprivmsg $nl "LEDs are watching you on [\
+               join [lsort [array names wch]] ","]: [join $l " "]"
+    }
+}
+
+proc msg_JOIN {p c chan} {
+    prefix_nick
+    set nl [irctolower $n]
+    chan_shortly $chan
+    warn_pref $n
+}
+proc msg_PART {p c chan} { chan_shortly $chan }
+proc msg_KILL {p c user why} { allchans_shortly }
+proc msg_QUIT {p c why} { allchans_shortly }
+proc msg_NICK {p c newnick} { allchans_shortly; warn_pref $newnick }
+proc msg_KICK {p c chans users comment} {
+    if {[llength $chans] > 1} {
+       allchans_shortly
+    } else {
+       chan_shortly [lindex $chans 0]
+    }
+}
 
 if {[catch {
     loadhelp