|
|
@@ -1,101 +1,290 @@
|
|
|
-#!/usr/bin/tclsh
|
|
|
+# watch.tcl
|
|
|
+#
|
|
|
+# This script watches users with the !watch command and informs the caller when
|
|
|
+# the user gets online or offline.
|
|
|
+#
|
|
|
+# Usage:
|
|
|
+# !watch add user watch user's status
|
|
|
+# !watch del user stop watching user's status
|
|
|
+# !watch check user check user's status once
|
|
|
+#
|
|
|
+# Enable for a channel with: .chanset #channel +watch
|
|
|
+# Disable for a channel with: .chanset #channel -watch
|
|
|
|
|
|
-proc watch_nick {nick host hand chan argv} {
|
|
|
+# tested versions, might run on earlier versions
|
|
|
+package require Tcl 8.6
|
|
|
+package require eggdrop 1.8.4
|
|
|
|
|
|
- global watch_nick_user
|
|
|
- set watch_nick_user $nick
|
|
|
+namespace eval ::watch {
|
|
|
+ # channel flag for enabling/disabling
|
|
|
+ setudef flag watch
|
|
|
+
|
|
|
+ # name of file for watched nicks
|
|
|
+ variable whoisFile "watchnick.db"
|
|
|
+
|
|
|
+ # list of pending whois requests
|
|
|
+ variable whoisList {}
|
|
|
+}
|
|
|
+
|
|
|
+# handle !watch command and call subcommands
|
|
|
+proc ::watch::watchNick {nick host hand chan argv} {
|
|
|
+ set usage "Usage: !watch <add|del|check> <nick>"
|
|
|
+
|
|
|
+ # check channel flag if enabled in this channel
|
|
|
+ if {![channel get $chan watch]} {
|
|
|
+ return 0
|
|
|
+ }
|
|
|
|
|
|
# check if there are enough parameters
|
|
|
if {[llength $argv] < 2} {
|
|
|
- puthelp "PRIVMSG $nick :Usage: !watch <add|del|chk> <nick>"
|
|
|
- #puts "PRIVMSG $nick :Usage: !watch <add|del> <nick>"
|
|
|
- return
|
|
|
+ puthelp "PRIVMSG $nick :$usage"
|
|
|
+ return 0
|
|
|
}
|
|
|
|
|
|
- # set some variables
|
|
|
+ # parse arguments and run command
|
|
|
set command [lindex $argv 0]
|
|
|
- set nick2watch [lindex $argv 1]
|
|
|
-
|
|
|
- if {$command == "add"} {
|
|
|
- watch_add $nick2watch
|
|
|
- } elseif {$command == "del" } {
|
|
|
- watch_del $nick2watch
|
|
|
- } elseif {$command == "chk" } {
|
|
|
- watch_chk $nick2watch
|
|
|
+ set whoisNick [lindex $argv 1]
|
|
|
+ switch $command {
|
|
|
+ "add" {
|
|
|
+ add $chan $nick $whoisNick
|
|
|
+ check $nick $whoisNick
|
|
|
+ }
|
|
|
+ "del" {
|
|
|
+ del $nick $whoisNick
|
|
|
+ }
|
|
|
+ "check" {
|
|
|
+ # set last status to "unknown" so reply is not filtered
|
|
|
+ lastStatus $nick $whoisNick "unknown"
|
|
|
+ check $nick $whoisNick
|
|
|
+ }
|
|
|
+ default {
|
|
|
+ puthelp "PRIVMSG $nick :$usage"
|
|
|
+ }
|
|
|
}
|
|
|
+ return 1
|
|
|
}
|
|
|
|
|
|
-proc watch_add {nick} {
|
|
|
|
|
|
- # file where to save nicks
|
|
|
- set nickdb "watch_nick.db"
|
|
|
+# check if entry a in whois file matches entry b
|
|
|
+proc ::watch::fileMatch {a b} {
|
|
|
+ # only check first two columns, ignore rest
|
|
|
+ if {[string compare -nocase [lindex $a 0] [lindex $b 0]] != 0} {
|
|
|
+ return 1
|
|
|
+ }
|
|
|
+ if {[string compare -nocase [lindex $a 1] [lindex $b 1]] != 0} {
|
|
|
+ return 1
|
|
|
+ }
|
|
|
+ return 0
|
|
|
+}
|
|
|
|
|
|
- # helper var to check if nick is already saved
|
|
|
- set found 0
|
|
|
+# get all entries from whois file
|
|
|
+proc ::watch::fileGet {} {
|
|
|
+ variable whoisFile
|
|
|
|
|
|
- # open file and start reading and comparing saved nicks with submitted one
|
|
|
- if ![catch {open $nickdb r} input] {
|
|
|
+ # read entries from whois file
|
|
|
+ set entlist ""
|
|
|
+ if {![catch {open $whoisFile r} input]} {
|
|
|
while {[gets $input line] >= 0} {
|
|
|
- if {[string compare -nocase $line $nick] == 0} {
|
|
|
- set found 1
|
|
|
- }
|
|
|
+ lappend entlist $line
|
|
|
}
|
|
|
close $input
|
|
|
}
|
|
|
|
|
|
- # insert nick into nickdb if it´s not already in it
|
|
|
- if { $found == 0} {
|
|
|
- if ![catch {open $nickdb a} output] {
|
|
|
- puts $output $nick
|
|
|
- close $output
|
|
|
+ return $entlist
|
|
|
+}
|
|
|
+
|
|
|
+# write all entries from entlist to whois file
|
|
|
+proc ::watch::filePut {entlist} {
|
|
|
+ variable whoisFile
|
|
|
+
|
|
|
+ if {![catch {open $whoisFile w} output]} {
|
|
|
+ foreach e $entlist {
|
|
|
+ puts $output $e
|
|
|
}
|
|
|
+ close $output
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-proc watch_del {nick} {
|
|
|
+# handle !watch subcommand "add"
|
|
|
+proc ::watch::add {chan nick whoisNick} {
|
|
|
+ variable whoisFile
|
|
|
|
|
|
- # file where to save nicks
|
|
|
- set nickdb "watch_nick.db"
|
|
|
+ # watch entry being added
|
|
|
+ set ent "$nick $whoisNick unknown"
|
|
|
|
|
|
- # open file and read nicks from it (omit nick that´s about to get deleted)
|
|
|
- if ![catch {open $nickdb r} input] {
|
|
|
- while {[gets $input line] >= 0} {
|
|
|
- if {$line != $nick} {
|
|
|
- lappend nicklist $line
|
|
|
- }
|
|
|
+ # check if entry already exists
|
|
|
+ foreach line [fileGet] {
|
|
|
+ if {[fileMatch $line $ent] == 0} {
|
|
|
+ # user already being watched
|
|
|
+ return 0
|
|
|
}
|
|
|
- close $input
|
|
|
}
|
|
|
|
|
|
- # write nicks to the file again
|
|
|
- if ![catch {open $nickdb w} output] {
|
|
|
- foreach entry $nicklist {
|
|
|
- puts $output $entry
|
|
|
- }
|
|
|
+ # new watch entry, insert nick into nickdb
|
|
|
+ if {![catch {open $whoisFile a} output]} {
|
|
|
+ puts $output $ent
|
|
|
close $output
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+# handle !watch subcommand "del"
|
|
|
+proc ::watch::del {nick whoisNick} {
|
|
|
+ # watch entry being deleted
|
|
|
+ set ent "$nick $whoisNick"
|
|
|
+
|
|
|
+ # find entry in whois file that's about to get deleted
|
|
|
+ set entlist [fileGet]
|
|
|
+ set i 0
|
|
|
+ foreach line $entlist {
|
|
|
+ if {[fileMatch $line $ent] == 0} {
|
|
|
+ # delete entry and write everything back to whois file
|
|
|
+ filePut [lreplace $entlist $i $i]
|
|
|
+ return
|
|
|
+ }
|
|
|
+ incr i
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+# update last status with newStatus and return old status
|
|
|
+proc ::watch::lastStatus {nick whoisNick newStatus} {
|
|
|
+ # get old status from file and remember line number
|
|
|
+ set entlist [fileGet]
|
|
|
+ set ent "$nick $whoisNick"
|
|
|
+ set last ""
|
|
|
+ set i 0
|
|
|
+ foreach line $entlist {
|
|
|
+ if {[fileMatch $line $ent] == 0} {
|
|
|
+ set last [lindex $line 2]
|
|
|
+ break
|
|
|
+ }
|
|
|
+ incr i
|
|
|
+ }
|
|
|
+
|
|
|
+ # if there was no entry in the file, stop and return an invalid status
|
|
|
+ if {$last == ""} {
|
|
|
+ return "none"
|
|
|
+ }
|
|
|
|
|
|
-proc watch_chk {nick2watch} {
|
|
|
+ # update last status in file
|
|
|
+ set entlist [lreplace $entlist $i $i "$nick $whoisNick $newStatus"]
|
|
|
+ filePut $entlist
|
|
|
|
|
|
- bind RAW - 401 watch_chk_nosuch
|
|
|
- bind RAW - 311 watch_chk_info
|
|
|
- putserv "WHOIS $nick2watch"
|
|
|
+ # return last status
|
|
|
+ return $last
|
|
|
}
|
|
|
|
|
|
-proc watch_chk_nosuch {var1 var2 var3} {
|
|
|
- global watch_nick_user
|
|
|
- puthelp "PRIVMSG $watch_nick_user :offline $var1 || $var2 || $var3"
|
|
|
- unbind RAW - 401 watch_chk_nosuch
|
|
|
- unbind RAW - 311 watch_chk_info
|
|
|
+# register for whois return codes
|
|
|
+proc ::watch::bindWhois {} {
|
|
|
+ # 401 - No Such User (offline)
|
|
|
+ # 311 - User Info (online)
|
|
|
+ bind RAW - 401 ::watch::checkReply
|
|
|
+ bind RAW - 311 ::watch::checkReply
|
|
|
}
|
|
|
|
|
|
-proc watch_chk_info {var1 var2 var3} {
|
|
|
- global watch_nick_user
|
|
|
- puthelp "PRIVMSG $watch_nick_user :online $var1 || $var2 || $var3"
|
|
|
- unbind RAW - 401 watch_chk_nosuch
|
|
|
- unbind RAW - 311 watch_chk_info
|
|
|
+
|
|
|
+# unregister whois return codes if no whois is pending
|
|
|
+proc ::watch::unbindWhois {} {
|
|
|
+ variable whoisList
|
|
|
+
|
|
|
+ # if no whois is pending any more, remove binds
|
|
|
+ if {[llength $whoisList] == 0} {
|
|
|
+ # 401 - No Such User (offline)
|
|
|
+ # 311 - User Info (online)
|
|
|
+ unbind RAW - 401 ::watch::checkReply
|
|
|
+ unbind RAW - 311 ::watch::checkReply
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
-bind pub - !watch watch_nick
|
|
|
+# add entry with nick and whoisNick to whois list
|
|
|
+proc ::watch::addWhois {nick whoisNick} {
|
|
|
+ variable whoisList
|
|
|
+
|
|
|
+ lappend whoisList "$nick $whoisNick"
|
|
|
+}
|
|
|
+
|
|
|
+# remove entry with nick and whoisNick from whois list
|
|
|
+proc ::watch::delWhois {nick whoisNick} {
|
|
|
+ variable whoisList
|
|
|
+
|
|
|
+ set i [lsearch $whoisList "$nick $whoisNick"]
|
|
|
+ set whoisList [lreplace $whoisList $i $i]
|
|
|
+}
|
|
|
+
|
|
|
+# find name of user who issued the whois for whoisNick
|
|
|
+proc ::watch::findWhois {whoisNick} {
|
|
|
+ variable whoisList
|
|
|
+
|
|
|
+ set nick ""
|
|
|
+ foreach e $whoisList {
|
|
|
+ if {$whoisNick == [lindex $e 1]} {
|
|
|
+ set nick [lindex $e 0]
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return $nick
|
|
|
+}
|
|
|
+
|
|
|
+# handle !watch subcommand "check"
|
|
|
+proc ::watch::check {nick whoisNick} {
|
|
|
+ variable whoisList
|
|
|
+
|
|
|
+ # register events
|
|
|
+ bindWhois
|
|
|
+
|
|
|
+ # add entry to whois list
|
|
|
+ addWhois $nick $whoisNick
|
|
|
+
|
|
|
+ # whois user
|
|
|
+ putserv "WHOIS $whoisNick"
|
|
|
+
|
|
|
+ return 1
|
|
|
+}
|
|
|
+
|
|
|
+# handle server replies of whois commands
|
|
|
+proc ::watch::checkReply {from keyword txt} {
|
|
|
+ variable whoisList
|
|
|
+ set whoisNick [lindex [split $txt] 1]
|
|
|
+
|
|
|
+ # set state according to reply keyword
|
|
|
+ set state "offline"
|
|
|
+ if {$keyword == 311} {
|
|
|
+ set state "online"
|
|
|
+ }
|
|
|
+
|
|
|
+ # find entry in whois list
|
|
|
+ set nick [findWhois $whoisNick]
|
|
|
+ if {$nick == ""} {
|
|
|
+ putlog "Error finding entry in whoisList."
|
|
|
+ return 0
|
|
|
+ }
|
|
|
+
|
|
|
+ # remove entry from whois list
|
|
|
+ delWhois $nick $whoisNick
|
|
|
+ unbindWhois
|
|
|
+
|
|
|
+ # update and get last status of watched user
|
|
|
+ set last [lastStatus $nick $whoisNick $state]
|
|
|
+ if {$last == $state} {
|
|
|
+ return 0
|
|
|
+ }
|
|
|
+
|
|
|
+ puthelp "PRIVMSG $nick :$whoisNick is $state."
|
|
|
+ return 0
|
|
|
+}
|
|
|
+
|
|
|
+# periodically check watched nicks
|
|
|
+proc ::watch::periodic {minute hour day month weekday} {
|
|
|
+ # check each entry in whois file
|
|
|
+ set entlist [fileGet]
|
|
|
+ foreach e $entlist {
|
|
|
+ set nick [lindex $e 0]
|
|
|
+ set whoisNick [lindex $e 1]
|
|
|
+ check $nick $whoisNick
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+namespace eval ::watch {
|
|
|
+ bind pub - !watch ::watch::watchNick
|
|
|
+ bind cron - "*/1 * * * *" ::watch::periodic
|
|
|
+}
|