watch.tcl 6.2 KB

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