watch.tcl 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. # watch.tcl
  2. #
  3. # This script watches users with the !watch command and informs the caller when
  4. # the user gets online or offline.
  5. #
  6. # Usage:
  7. # !watch add user watch user's status
  8. # !watch del user stop watching user's status
  9. # !watch check user check user's status once
  10. #
  11. # Enable for a channel with: .chanset #channel +watch
  12. # Disable for a channel with: .chanset #channel -watch
  13. # tested versions, might run on earlier versions
  14. package require Tcl 8.6
  15. package require eggdrop 1.8.4
  16. namespace eval ::watch {
  17. # channel flag for enabling/disabling
  18. setudef flag watch
  19. # name of file for watched nicks
  20. variable whoisFile "watchnick.db"
  21. # list of pending whois requests
  22. variable whoisList {}
  23. }
  24. # handle !watch command and call subcommands
  25. proc ::watch::watchNick {nick host hand chan argv} {
  26. set usage "Usage: !watch <add|del|check> <nick>"
  27. # check channel flag if enabled in this channel
  28. if {![channel get $chan watch]} {
  29. return 0
  30. }
  31. # check if there are enough parameters
  32. if {[llength $argv] < 2} {
  33. puthelp "PRIVMSG $nick :$usage"
  34. return 0
  35. }
  36. # parse arguments and run command
  37. set command [lindex $argv 0]
  38. set whoisNick [lindex $argv 1]
  39. switch $command {
  40. "add" {
  41. add $chan $nick $whoisNick
  42. check $nick $whoisNick
  43. }
  44. "del" {
  45. del $nick $whoisNick
  46. }
  47. "check" {
  48. # set last status to "unknown" so reply is not filtered
  49. lastStatus $nick $whoisNick "unknown"
  50. check $nick $whoisNick
  51. }
  52. default {
  53. puthelp "PRIVMSG $nick :$usage"
  54. }
  55. }
  56. return 1
  57. }
  58. # check if entry a in whois file matches entry b
  59. proc ::watch::fileMatch {a b} {
  60. # only check first two columns, ignore rest
  61. if {[string compare -nocase [lindex $a 0] [lindex $b 0]] != 0} {
  62. return 1
  63. }
  64. if {[string compare -nocase [lindex $a 1] [lindex $b 1]] != 0} {
  65. return 1
  66. }
  67. return 0
  68. }
  69. # get all entries from whois file
  70. proc ::watch::fileGet {} {
  71. variable whoisFile
  72. # read entries from whois file
  73. set entlist ""
  74. if {![catch {open $whoisFile r} input]} {
  75. while {[gets $input line] >= 0} {
  76. lappend entlist $line
  77. }
  78. close $input
  79. }
  80. return $entlist
  81. }
  82. # write all entries from entlist to whois file
  83. proc ::watch::filePut {entlist} {
  84. variable whoisFile
  85. if {![catch {open $whoisFile w} output]} {
  86. foreach e $entlist {
  87. puts $output $e
  88. }
  89. close $output
  90. }
  91. }
  92. # handle !watch subcommand "add"
  93. proc ::watch::add {chan nick whoisNick} {
  94. variable whoisFile
  95. # watch entry being added
  96. set ent "$nick $whoisNick unknown"
  97. # check if entry already exists
  98. foreach line [fileGet] {
  99. if {[fileMatch $line $ent] == 0} {
  100. # user already being watched
  101. return 0
  102. }
  103. }
  104. # new watch entry, insert nick into nickdb
  105. if {![catch {open $whoisFile a} output]} {
  106. puts $output $ent
  107. close $output
  108. }
  109. }
  110. # handle !watch subcommand "del"
  111. proc ::watch::del {nick whoisNick} {
  112. # watch entry being deleted
  113. set ent "$nick $whoisNick"
  114. # find entry in whois file that's about to get deleted
  115. set entlist [fileGet]
  116. set i 0
  117. foreach line $entlist {
  118. if {[fileMatch $line $ent] == 0} {
  119. # delete entry and write everything back to whois file
  120. filePut [lreplace $entlist $i $i]
  121. return
  122. }
  123. incr i
  124. }
  125. }
  126. # update last status with newStatus and return old status
  127. proc ::watch::lastStatus {nick whoisNick newStatus} {
  128. # get old status from file and remember line number
  129. set entlist [fileGet]
  130. set ent "$nick $whoisNick"
  131. set last ""
  132. set i 0
  133. foreach line $entlist {
  134. if {[fileMatch $line $ent] == 0} {
  135. set last [lindex $line 2]
  136. break
  137. }
  138. incr i
  139. }
  140. # if there was no entry in the file, stop and return an invalid status
  141. if {$last == ""} {
  142. return "none"
  143. }
  144. # update last status in file
  145. set entlist [lreplace $entlist $i $i "$nick $whoisNick $newStatus"]
  146. filePut $entlist
  147. # return last status
  148. return $last
  149. }
  150. # register for whois return codes
  151. proc ::watch::bindWhois {} {
  152. # 401 - No Such User (offline)
  153. # 311 - User Info (online)
  154. bind RAW - 401 ::watch::checkReply
  155. bind RAW - 311 ::watch::checkReply
  156. }
  157. # unregister whois return codes if no whois is pending
  158. proc ::watch::unbindWhois {} {
  159. variable whoisList
  160. # if no whois is pending any more, remove binds
  161. if {[llength $whoisList] == 0} {
  162. # 401 - No Such User (offline)
  163. # 311 - User Info (online)
  164. unbind RAW - 401 ::watch::checkReply
  165. unbind RAW - 311 ::watch::checkReply
  166. }
  167. }
  168. # add entry with nick and whoisNick to whois list
  169. proc ::watch::addWhois {nick whoisNick} {
  170. variable whoisList
  171. lappend whoisList "$nick $whoisNick"
  172. }
  173. # remove entry with nick and whoisNick from whois list
  174. proc ::watch::delWhois {nick whoisNick} {
  175. variable whoisList
  176. set i [lsearch $whoisList "$nick $whoisNick"]
  177. set whoisList [lreplace $whoisList $i $i]
  178. }
  179. # find name of user who issued the whois for whoisNick
  180. proc ::watch::findWhois {whoisNick} {
  181. variable whoisList
  182. set nick ""
  183. foreach e $whoisList {
  184. if {$whoisNick == [lindex $e 1]} {
  185. set nick [lindex $e 0]
  186. break
  187. }
  188. }
  189. return $nick
  190. }
  191. # handle !watch subcommand "check"
  192. proc ::watch::check {nick whoisNick} {
  193. variable whoisList
  194. # register events
  195. bindWhois
  196. # add entry to whois list
  197. addWhois $nick $whoisNick
  198. # whois user
  199. putserv "WHOIS $whoisNick"
  200. return 1
  201. }
  202. # handle server replies of whois commands
  203. proc ::watch::checkReply {from keyword txt} {
  204. variable whoisList
  205. set whoisNick [lindex [split $txt] 1]
  206. # set state according to reply keyword
  207. set state "offline"
  208. if {$keyword == 311} {
  209. set state "online"
  210. }
  211. # find entry in whois list
  212. set nick [findWhois $whoisNick]
  213. if {$nick == ""} {
  214. putlog "Error finding entry in whoisList."
  215. return 0
  216. }
  217. # remove entry from whois list
  218. delWhois $nick $whoisNick
  219. unbindWhois
  220. # update and get last status of watched user
  221. set last [lastStatus $nick $whoisNick $state]
  222. if {$last == $state} {
  223. return 0
  224. }
  225. puthelp "PRIVMSG $nick :$whoisNick is $state."
  226. return 0
  227. }
  228. # periodically check watched nicks
  229. proc ::watch::periodic {minute hour day month weekday} {
  230. # check each entry in whois file
  231. set entlist [fileGet]
  232. foreach e $entlist {
  233. set nick [lindex $e 0]
  234. set whoisNick [lindex $e 1]
  235. check $nick $whoisNick
  236. }
  237. }
  238. namespace eval ::watch {
  239. bind pub - !watch ::watch::watchNick
  240. bind cron - "*/1 * * * *" ::watch::periodic
  241. putlog "Loaded watch.tcl"
  242. }