home *** CD-ROM | disk | FTP | other *** search
/ HaCKeRz KrOnIcKLeZ 3 / HaCKeRz_KrOnIcKLeZ.iso / scriptz / news04.tcl < prev    next >
Text File  |  1996-04-23  |  18KB  |  551 lines

  1. #
  2. # NEWS v0.4
  3. #   eggdrop/tcl script by eden <brookes@cs.uq.edu.au>
  4. # This script adds commands so that users can post news for others
  5. # to read.  It works similarly to the notes system, except news can be
  6. # read by anybody.  Users can add news messages, erase news messages, view
  7. # them privately (by NOTICE) or display them to the channel.  A timer is
  8. # included that alerts users on channel to the presence of notes.
  9. #
  10. # It is based on the original news script by Robey, and the timer stuff was
  11. # based on the tcl.topic script by Brian_C <s0206377@cc.ysu.edu>.
  12. #
  13. #
  14. # /msg <bot> news add <message...>
  15. #   (requires to be known by the bot)
  16. #   adds a message to the news file on the bot
  17. #
  18. # /msg <bot> news erase #
  19. #   (requires to be known by the bot)
  20. #   erases the message numbered # from the news file.  Only the user who
  21. #   put the message there may erase it, except for masters who can erase
  22. #   any message
  23. #
  24. # /msg <bot> news read \[#\]
  25. #   (anyone can do)
  26. #   shows the current news file, via notice.  The item number # is
  27. #   optional.  If there, only that item is shown.
  28. #
  29. # /msg <bot> news show \[#\]
  30. #   (anyone can do)
  31. #   shows the current news file publicly, to the channel.  Will not show
  32. #   the newsfile more than once a minute, to prevent flooding.  The item
  33. #   number is optional.  If there, only that item is shown.
  34. #
  35. # /msg <bot> news announce
  36. #   (anyone can do)
  37. #   announces the presence of news to the channel (if there is any)
  38. #
  39. # /msg <bot> news check
  40. #   (anyone can do)
  41. #   sends a private notice to a user telling them how much news there is
  42. #
  43. # /msg <bot> news reset
  44. #   (requires +m)
  45. #   resets the counter associated with the news system but does not 
  46. #   modify the news file itself.  Shouldn't be necessary in an ideal world :)
  47. #
  48. # /msg <bot> news clear
  49. #   (requires +m)
  50. #   erases all news in the news file and resets the counter
  51. #
  52. # There is also a timer included, so that once an hour, on the hour, the
  53. # bot displays an announcement that there is news to be read (if there is).
  54. # It doesn't display the news to the channel unless asked.
  55. #
  56. # All TCL variables and internal procedures associated with the news system
  57. # start with "news"
  58. #
  59. # TODO:
  60. #   - DCC bindings so the news can be manipulated on the party line
  61. #
  62. # HISTORY:
  63. #   - 0.3 -> 0.4
  64. #     Changed basic way of informing users of news.  Now it is done with an
  65. #     on_join binding.  It keeps a record for EACH user of the timestamp of
  66. #     the last message they read, and only shows them unread messages by
  67. #     default.  This info is stored in the user's "comment" field.
  68. #     Added "check" message command.
  69. #
  70. #   - 0.2 ->0.3
  71. #     Added "announce" message command 
  72. #     When users read news, it now also tells them how to get further help
  73. #     Merged the internal procs news_select and news_print to just news_print
  74. #     Added feedback to the "reset" command
  75. #     
  76. #   - 0.2
  77. #     Original version 0.2 to distinguish it from Robey's version
  78.  
  79.  
  80. # You may want to move this line to your config-file for easier changing
  81. #set newsfile ".news"
  82.  
  83. # You can choose either to use the "umbrella" news binding, or to have
  84. # separate bindings for each command, or both.  I use both because users 
  85. # are dumb :)
  86. bind msg - news msg_news
  87. bind msg - addnews news_add
  88. bind msg - delnews news_del
  89. bind msg - erasenews news_del
  90. bind msg - readnews news_read
  91. bind msg - shownews news_show
  92. bind msg - announcenews news_announce
  93. bind msg - checknews news_check
  94. bind msg - resetnews news_reset
  95. bind msg - clearnews news_clear
  96.  
  97. # The following line makes the script send a notice to each user as they
  98. # join the channel advising them of the unread channel news
  99. bind join - *@* news_onjoin
  100.  
  101. # To DISABLE the timer, uncomment the following line.
  102. # If you have already loaded the script and it is running, you will have to
  103. # find and manually kill the timer (".tcl timers" then ".tcl killtimer #")
  104. #set set_news_running 1
  105.  
  106.  
  107. #-=-=-=-=-=-=-=-=- YOU SHOULD NOT NEED TO EDIT BELOW THIS LINE -=-=-=-=-=-=-=-=-
  108.  
  109.  
  110. # These are internal variables and shouldn't be changed here
  111. set news_ncount -1
  112. set news_newnews 0
  113. set news_lastseen 0
  114. set news_biggest_ts 0
  115.  
  116.  
  117. # Is there any news?
  118. proc news_thereisnews {} {
  119.   global newsfile 
  120.   if {[news_count] == 0} {
  121.     return 0
  122.   } else {
  123.     return 1
  124.   }
  125. }
  126.  
  127. # How many news items are there?  The first time this is called, it will
  128. # read through the file and set the value of news_ncount.  In future, the
  129. # value of the variable will be used, rather than reading the file each time.
  130. # Also updates the news_biggest_ts
  131. proc news_count {} {
  132.   global newsfile news_ncount news_biggest_ts
  133.   # only ever go through reading the file once -- the first time it's called
  134.   if {$news_ncount == -1} {
  135.     set news_ncount 0
  136.     set fd [open $newsfile r]
  137.     while {![eof $fd]} {
  138.       set inp [gets $fd]
  139.       if {[eof $fd]} {break}
  140.       if {[string trim $inp " "] == ""} {continue}
  141.       if {[lindex $inp 1] > $news_biggest_ts} {
  142.     set news_biggest_ts [lindex $inp 1]
  143.       }
  144.       incr news_ncount
  145.     }
  146.     close $fd
  147.   } 
  148.   return $news_ncount
  149. }
  150.  
  151.  
  152. # Internal procedure that, given a users handle, tells how many news items
  153. # there are in the news file that they haven't read
  154. proc news_unread {hand} {
  155.   global newsfile
  156.   set unread 0
  157.   set user_ts [news_get_ts $hand]
  158.   set fd [open $newsfile r]
  159.   while {![eof $fd]} {
  160.     set inp [gets $fd]
  161.     if {[eof $fd]} {break}
  162.     if {[string trim $inp " "] == ""} {continue}
  163.     if {[lindex $inp 1] > $user_ts} {
  164.       incr unread
  165.     }
  166.   }
  167.   close $fd
  168.   return $unread
  169. }
  170.  
  171.  
  172.  
  173. # Internal procedure to print out news from the file, since it's done more
  174. # than once.  'tochannel' should be 1 if the output is going to a channel, 
  175. # or 0 if it is going privately to a user.  'item' should = 0 if all items
  176. # are to be printed, or a single number to print a specific item.
  177. # If the timestamp is non-zero, only news items more recent than this
  178. # timestamp are printed.
  179. proc news_print {tochannel nick item timestamp} {
  180.   global newsfile 
  181.   set next 1
  182.   set fd [open $newsfile r]
  183.   while {![eof $fd]} {
  184.     set inp [gets $fd]
  185.     if {[eof $fd]} {break}
  186.     if {[string trim $inp " "] == ""} {continue}
  187.     if {($item == 0) || ($item == $next)} {
  188.       # timestamp == 0 to show all items
  189.       if {[lindex $inp 1] > $timestamp} {  
  190.         set who [lindex $inp 0]
  191.         set date [lrange [ctime [lindex $inp 1]] 0 2]
  192.         set news [lrange $inp 2 end ]
  193.         # This gets messy because there's four possible cases
  194.         if {$tochannel} {
  195.       if {$item == 0} {
  196.             putserv [format "PRIVMSG %s :%d. \[%s\] %s \(%s\)" $nick $next \
  197.         $who $news $date]
  198.           } else { putserv [format "PRIVMSG %s :\[%s\] %s \(%s\)" $nick $who \
  199.         $news $date]
  200.           }
  201.         } else {
  202.       if {$item == 0} {
  203.             puthelp [format "NOTICE %s :%d. \[%s\] %s \(%s\)" $nick $next \
  204.             $who $news $date]
  205.           } else { puthelp [format "NOTICE %s :\[%s\] %s \(%s\)" $nick $who \
  206.             $news $date]
  207.           }
  208.         }
  209.       }
  210.     }
  211.     incr next
  212.   }
  213.   close $fd
  214.   return 1
  215. }
  216.  
  217.  
  218. # Get a users "last read" timestamp from the userfile
  219. proc news_get_ts {hand} {
  220.   set usercomment [getcomment $hand]
  221.   set have_ts [scan $usercomment "%d " the_ts]
  222.   if {$have_ts > 0} { 
  223.     return $the_ts
  224.   } else {
  225.     return 0
  226.   }
  227. }
  228.  
  229.  
  230. # Set a users "last read" timestamp in the userfile
  231. proc news_set_ts {hand new_ts} {
  232.   set usercomment [getcomment $hand]
  233.   set have_ts [scan $usercomment "%d" old_ts]
  234.   if {$have_ts == 0} { set the_comment $usercomment }
  235.   if {$have_ts == 2} { set the_comment [string range $usercomment [string first " " $usercomment] end] }
  236.   set new_comment $new_ts
  237.   if {($have_ts == 2) || ($have_ts == 0)} {
  238.     append new_comment " " $the_comment
  239.   }
  240.   setcomment $hand $new_comment
  241.   return
  242. }
  243.  
  244.  
  245. # Happens when any user joins a channel
  246. proc news_onjoin {nick uhost hand} {
  247.   global botnick channel
  248.   # If not known by the bot, return immediately
  249.   if {[string compare $hand "*"] == 0} {return}
  250.   set unread [news_unread $hand]
  251.   if {$unread} {
  252.     putserv "NOTICE $nick :There is $channel news ($unread unread, [news_count] total).  \002/msg $botnick NEWS READ\002"
  253.   }
  254.   return
  255. }
  256.  
  257.  
  258. # Announce that there is news (if there is any)
  259. proc news_int_announce {} {
  260.   global channel botnick news_newnews
  261.   if {[news_thereisnews]} {
  262.     if {$news_newnews} {
  263.       putserv "PRIVMSG $channel :\002There is \026NEW\026 $channel news since the last hour \([news_count] items\)!  To display it, /msg $botnick NEWS READ\002"
  264.       putlog "NEWS: announced NEW news to channel"
  265.     } else {
  266.       putserv "PRIVMSG $channel :\002There is $channel news \([news_count] items\).  To display it, /msg $botnick NEWS READ\002"
  267.       putlog "NEWS: announced news to channel"
  268.     }
  269.   } else {
  270.     putlog "NEWS: no news to announce"
  271.     return 0
  272.   }
  273.   set news_newnews 0
  274.   return 1
  275. }
  276.  
  277.  
  278. # Announce the news once an hour
  279. proc news_timer {} {  }
  280.  
  281.  
  282. # Make sure it is announced on the hour, not at any random time
  283. proc news_sync {} {
  284.   set time_ending [string range [time] 2 4]
  285.   if { $time_ending == ":00" } {
  286.     news_timer
  287.   } {
  288.     timer 1 news_sync
  289.   }
  290. }
  291.  
  292.  
  293. # Start the timer running if it's not already
  294. # NB. This is NOT a procedure, it is global.
  295. if {![info exists set_news_running]} {
  296.   news_sync
  297.   set set_news_running 1
  298. }
  299.  
  300.  
  301. # Add a news item
  302. proc news_add {nick uhost hand arg} {
  303.   global newsfile botnick news_ncount news_newnews news_biggest_ts
  304.   if {![validuser $hand]} {
  305.     puthelp "NOTICE $nick :Please introduce yourself to me first with \002/msg $botnick hello\002"
  306.     return 0
  307.   }
  308.   set message [lrange $arg 1 end]
  309.   if {$message == ""} {
  310.     putserv "NOTICE $nick :Usage: /msg $botnick news add <your news...>"
  311.     putserv "NOTICE $nick :  adds something to the news file"
  312.     return 0
  313.   }
  314.   set fd [open $newsfile a]
  315.   puts $fd [format "%s %s %s" $hand [unixtime] $message]
  316.   close $fd
  317.   # Do not just use 'incr' in case news_count currently = -1
  318.   set news_ncount [expr [news_count] + 1]
  319.   set news_newnews 1
  320.   set news_biggest_ts [unixtime]
  321.   putserv "NOTICE $nick :Added to the news file, thanks!"
  322.   return 1
  323. }
  324.  
  325.  
  326. # Delete a news item
  327. proc news_del {nick uhost hand arg} {
  328.   global newsfile botnick news_ncount 
  329.   if {![validuser $hand]} {
  330.     puthelp "NOTICE $nick :Please introduce yourself to me first with \002/msg $botnick hello\002"
  331.     return 0
  332.   }
  333.   set otherargs [lrange $arg 1 end]
  334.   if {[scan $otherargs "%d" which] != 1} {
  335.     news_help $nick $uhost $hand $arg ; return 0 }
  336.   if {($which < 1) || ($which > [news_count])} {
  337.     puthelp "NOTICE $nick :The item number must be between 1 and [news_count]"
  338.     return 0
  339.   }
  340.   # We will copy the entries from the original file to a temp file, except
  341.   # for the one to be deleted, then rename the temp file to be the original.
  342.   # This is how eggdrop deletes notes, so we will use it too.
  343.   set next 1
  344.   set fd [open $newsfile r]
  345.   set newfd [open "${newsfile}~new" w]
  346.   while {![eof $fd]} {
  347.     set inp [gets $fd]
  348.     if {[eof $fd]} {break}
  349.     if {[string trim $inp " "] == ""} {continue}
  350.     set who [lindex $inp 0]
  351.     if {$next == $which} {
  352.       # Can only delete your own news unless you're a master
  353.       if {([string tolower $who] != [string tolower $hand]) &&
  354.       (![matchattr $hand "m"])} {
  355.     puthelp "NOTICE $nick :You can\'t delete other people\'s news!"
  356.         puts $newfd $inp
  357.       } else {
  358.     # delete it -- ie, do not copy it -- just update the counter
  359.     # Do not use 'incr' just in case it = -1
  360.     set news_ncount [expr [news_count] - 1]
  361.       }
  362.     } else {
  363.       puts $newfd $inp
  364.     }
  365.     incr next
  366.   }
  367.   close $fd
  368.   close $newfd
  369.   exec /bin/mv ${newsfile}~new $newsfile
  370.   puthelp "NOTICE $nick :Deleted from the news file, thanks!"
  371.   return 1
  372. }
  373.  
  374.  
  375.  
  376. # Read the channel news privately
  377. proc news_read {nick uhost hand arg} {
  378.   global newsfile channel botnick news_biggest_ts
  379.   set otherargs [lrange $arg 1 end]
  380.   if {[string trim $otherargs] != ""} {
  381.     set targ [scan $otherargs "%s " textarg]
  382.     if {[string compare [string tolower $textarg] "all"] == 0} {
  383.       if {[news_thereisnews]} {
  384.         puthelp "NOTICE $nick :\002All news on $channel for [date] ...\002"
  385.         news_print 0 $nick 0 0
  386.         puthelp "NOTICE $nick :\002--- end ---\002"
  387.       } else {
  388.         puthelp "NOTICE $nick :Sorry, there is no news at the moment!"
  389.       } 
  390.       puthelp "NOTICE $nick :For help with other news commands, \002/msg \
  391.       $botnick HELP NEWS\002"
  392.       return 1
  393.     }
  394.     set havearg [scan $otherargs "%d" which]
  395.     if {$havearg} {
  396.       if {($which < 1) || ($which > [news_count])} {
  397.         puthelp "NOTICE $nick :The item number must be between 1 and [news_count]"
  398.         return 0
  399.       } else {
  400.         news_print 0 $nick $which 0
  401.         return 1
  402.       }
  403.     }
  404.   }
  405.   # By the time we are here, it must have no arguments - display only new ones!
  406.   if {[news_thereisnews]} {
  407.     set user_ts [news_get_ts $hand]
  408.     if {$user_ts >= $news_biggest_ts} {
  409.       puthelp "NOTICE $nick :Sorry, there is no new news at the moment!"
  410.       puthelp "NOTICE $nick :To read all news, \002/msg $botnick NEWS READ ALL\002"
  411.     } else {
  412.       puthelp "NOTICE $nick :\002New news for $nick on $channel for [date] ...\002"
  413.       news_print 0 $nick 0 $user_ts
  414.       puthelp "NOTICE $nick :\002--- end ---\002"
  415.     news_set_ts $hand $news_biggest_ts
  416.     }
  417.   } else {
  418.     puthelp "NOTICE $nick :Sorry, there is no news at the moment!"
  419.   }
  420.   puthelp "NOTICE $nick :For help with other news commands, \002/msg $botnick HELP NEWS\002"
  421.   return 1
  422. }  
  423.  
  424.  
  425. # Show the channel news publicly
  426. proc news_show {nick uhost hand arg} {
  427.   global newsfile botnick channel news_lastseen
  428.   set otherargs [lrange $arg 1 end]
  429.   if {[string trim $otherargs] != ""} {
  430.     set havearg [scan $otherargs "%d" which]
  431.     if {$havearg} {
  432.       if {($which < 1) || ($which > [news_count])} {
  433.         puthelp "NOTICE $nick :The item number must be between 1 and [news_count]"
  434.         return 0
  435.       } else {
  436.         news_print 1 $channel $which 0
  437.         return 1
  438.       }
  439.     }
  440.   }
  441.   # By the time we are here, it must have no arguments - display all
  442.   if {([news_thereisnews]) && ([expr ([unixtime] - $news_lastseen) > 60])} {
  443.     putserv "PRIVMSG $channel :\002NEWS on $channel for [date] ...\002 \(as requested by $nick\)"
  444.     news_print 1 $channel 0 0
  445.     putserv "PRIVMSG $channel :\002--- end ---\002"
  446.     putserv "PRIVMSG $channel :\002/msg $botnick HELP NEWS for instructions on how to use news\002"
  447.     set news_lastseen [unixtime]
  448.   } elseif {[news_thereisnews]} {
  449.     puthelp "NOTICE $nick :News already displayed.  I won\'t display more than once a minute."
  450.     puthelp "NOTICE $nick :To see the news again privately, /msg $botnick NEWS READ"
  451.     return 0
  452.   } else {
  453.     puthelp "NOTICE $nick :Sorry, there is no news at the moment!"
  454.   }
  455.   return 1
  456. }  
  457.  
  458.  
  459. # Announce the availability of news to the channel
  460. proc news_announce {nick uhost hand arg} {
  461.   if {[news_int_announce]} {
  462.     puthelp "NOTICE $nick :Announced news availability to the channel."
  463.   } else {
  464.     puthelp "NOTICE $nick :No news to announce!"
  465.   }
  466.   return 1
  467. }
  468.  
  469.  
  470.  
  471. # Reset the counter associated with the news file.
  472. proc news_reset {nick uhost hand arg} {
  473.   global news_ncount news_biggest_ts
  474.   if {![matchattr $hand "m"]} {
  475.     puthelp "NOTICE $nick :You don't have permission to reset the news file."
  476.     return 0
  477.   }
  478.   # Just reset the counter, don't touch the file
  479.   set news_ncount -1
  480.   set news_biggest_ts 0
  481.   puthelp "NOTICE $nick :OK, reset."
  482.   return 1
  483. }
  484.  
  485.  
  486. # Erase all entries in the news file
  487. proc news_clear {nick uhost hand arg} {
  488.   global newsfile news_ncount news_newnews
  489.   if {![matchattr $hand "m"]} {
  490.     puthelp "NOTICE $nick :You don't have permission to clear the news file."
  491.     return 0
  492.   }
  493.   set fd [open $newsfile w]
  494.   puts $fd " "
  495.   close $fd
  496.   set news_ncount -1
  497.   set news_newnews 0
  498.   putserv "NOTICE $nick :Okay, news file cleared!"
  499.   return 1
  500. }
  501.  
  502.  
  503. # Check what news there is to be read
  504. proc news_check {nick uhost hand arg} {
  505.   global botnick channel
  506.   set unread [news_unread $hand]
  507.   if {$unread} {
  508.     putserv "NOTICE $nick :There is $channel news ($unread unread, [news_count] total).  \002/msg $botnick NEWS READ\002"
  509.   }
  510.   retur1
  511. }
  512.  
  513.  
  514. # Display some help
  515. proc news_help {nick uhost hand arg} {
  516.   puthelp "NOTICE $nick :Usage: NEWS ADD <your news>"
  517.   puthelp "NOTICE $nick :       NEWS ERASE #"
  518.   puthelp "NOTICE $nick :       NEWS READ \[#\]"
  519.   puthelp "NOTICE $nick :       NEWS SHOW \[#\]"
  520.   puthelp "NOTICE $nick :       NEWS ANNOUNCE"
  521.   if {[matchattr $hand "m"]} {
  522.   puthelp "NOTICE $nick :       NEWS RESET" 
  523.   puthelp "NOTICE $nick :       NEWS CLEAR" }
  524.   return 0
  525. }
  526.  
  527.  
  528. # The main binding - does the dispatching of requests
  529. proc msg_news {nick uhost hand arg} {
  530.   switch [string tolower [lindex $arg 0]] {
  531.     "add"     {set r [news_add $nick $uhost $hand $arg]}
  532.     "new"     {set r [news_add $nick $uhost $hand $arg]}
  533.     "del"     {set r [news_del $nick $uhost $hand $arg]}
  534.     "delete"     {set r [news_del $nick $uhost $hand $arg]}
  535.     "erase"     {set r [news_del $nick $uhost $hand $arg]}
  536.     "read"     {set r [news_read $nick $uhost $hand $arg]}
  537.     "show"     {set r [news_show $nick $uhost $hand $arg]}
  538.     "ann"     {set r [news_announce $nick $uhost $hand $arg]}
  539.     "announce"     {set r [news_announce $nick $uhost $hand $arg]}
  540.     "check"     {set r [news_check $nick $uhost $hand $arg]}
  541.     "reset"     {set r [news_reset $nick $uhost $hand $arg]}
  542.     "clear"     {set r [news_clear $nick $uhost $hand $arg]}
  543.     default    {set r [news_help $nick $uhost $hand $arg]}
  544.   }
  545.   return $r
  546. }
  547.  
  548.  
  549. putlog "NEWS script v0.4 loaded successfully!"
  550.  
  551.