source: TopicEngine2/TopicEngine.tcl @ 1

Revision 1, 52.9 KB checked in by notopic, 6 years ago (diff)

Import

Line 
1#         Script : TopicEngine v1
2#                  Copyright 2001-3 James Seward
3#                   $Id: TopicEngine.tcl,v 1.11 2003/02/05 23:24:37 jamesoff Exp $
4#
5#       Testing
6#      Platforms : Linux 2.4
7#                  Eggdrop v1.6.4-13
8#
9#    Description : Advanced script to change/set/lock topics
10#                  See readme for full info
11#
12#
13# Author Contact :     Email - james@jamesoff.net
14#                  Home Page - http://www.jamesoff.net
15#                        IRC - Nick: JamesOff (EFNet)
16#                        ICQ - 1094325 (mention this script in your auth req, else you'll get ignored :)
17#
18#      Credit to : Dan Durrans, for coming up with the idea and feature list
19#                  #exeter and #ags people for testing it
20#
21#
22
23###############################################################################
24# TopicEngine - a topic management TCL script for eggdrops
25# Copyright (C) James Michael Seward 2000-2003
26#
27# This program is free software; you can redistribute it and/or modify
28# it under the terms of the GNU General Public License as published by
29# the Free Software Foundation; either version 2 of the License, or
30# (at your option) any later version.
31#
32# This program is distributed in the hope that it will be useful, but
33# WITHOUT ANY WARRANTY; without even the implied warranty of
34# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
35# General Public License for more details.
36#
37# You should have received a copy of the GNU General Public License
38# along with this program; if not, write to the Free Software
39# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
40###############################################################################
41
42#Don't change this unless you want to RUIN the script and destroy the world :)
43set topicEngineLoad 0
44
45# register the topicEngine channel flag
46setudef flag topicengine
47
48#
49# This is the path to the config file's directory
50# If you use a relative path, it starts from the directory your eggdrop runs in
51# No trailing slash!
52set topicConfigPath "scripts"
53
54catch {
55  source "${topicConfigPath}/TopicEngineSettings.tcl"
56  if {[info exists botnet-nick]} {
57    #load a bot-specific file
58    source "${topicConfigPath}/TopicEngineSettings_${botnet-nick}.tcl"
59  }
60}
61
62#####
63# Shouldn't need to change stuff below here (but you can if you need to and know
64# what you're doing)
65###################################################################################################
66
67set revision "20060108.1"
68
69#init some info
70set topicInfo(pop,version) $revision
71set backupInfo(pop,version) $revision
72set topicChannels [list]
73
74#split tracking
75set topicSplitChans [list]
76set topicLastSplit [clock seconds]
77set topicSplitDirty 0
78
79if {![info exists topicEngineOnline]} {
80  set topicEngineOnline 0
81}
82
83# init
84set loops 0
85set bufferDirty 0
86
87#####
88# topicInitArray
89#
90# Builds the topicInfo array with the default values
91# Won't rebuild if topicEngineOnline is 1 (to stop it overwriting
92# data that's in use
93#
94proc topicInitArray { {loaded 0} } {
95  global topicInfo topicEngineOnline
96
97  set onChannels [channels]
98  set hasError 0
99
100  #change the channels list to all lowercase
101  set newChannels [list]
102  foreach chan $onChannels {
103    #check if this channel is +topicengine
104    if [channel get $chan topicengine] {
105      lappend newChannels [string tolower $chan]
106    }
107  }
108  set topicChannels $newChannels
109
110  foreach chan $topicChannels {
111    set test ""
112    catch {
113      set test $topicInfo($chan,initialised)
114    }
115    if {$test == ""} {
116      #loading for the first time
117      putlog "topicengine: init topicInfo for $chan"
118      # These are the defaults, you may change them
119      # override them with values in TopicEngineSettings.tcl
120      # explainations of these settings are also in that file
121      set topicInfo($chan,leadIn) ""
122      set topicInfo($chan,leadOut) ""
123      #set topicInfo($chan,needOChan) 1
124      #set topicInfo($chan,needVChan) 0
125      #set topicInfo($chan,needOGlobal) 0
126      #set topicInfo($chan,needVGlobal) 0
127      #set topicInfo($chan,needOMode) 1
128      #set topicInfo($chan,needVMode) 0
129      #set topicInfo($chan,Tflag) 1
130      set topicInfo($chan,canFlags) "o|ov"
131      set topicInfo($chan,canModes) "ov"
132      set topicInfo($chan,cantFlags) "T"
133      set topicInfo($chan,topicBits) [list]
134      set topicInfo($chan,learnOnChange) 1
135      set topicInfo($chan,initialised) "1"
136     
137      #DO NOT CHANGE THESE
138      set topicInfo($chan,topic) ""
139      set topicInfo($chan,whoSet) [list]
140      set topicInfo($chan,whenSet) [list]
141      set topicInfo($chan,whenLastSet) 0
142      set topicInfo($chan,whoLastSet) 0
143      set topicInfo($chan,lock) [list 0 "" ""]
144    } else {
145      if {$loaded == 0} {
146        #sync the topics if we're online
147        catch {
148          if {[lsearch $onChannels $chan] >= 0} { 
149            #putlog "Updating topic in $chan"
150            setTopic $chan 
151          }
152          set blah 0
153        } err
154        if {$err != 0} {
155          putlog "topicengine: $chan not initialised! (is it new?)"
156          set topicInfo($chan,leadOut) ""
157          #set topicInfo($chan,needOChan) 1
158          #set topicInfo($chan,needVChan) 0
159          #set topicInfo($chan,needOGlobal) 0
160          #set topicInfo($chan,needVGlobal) 0
161          #set topicInfo($chan,needOMode) 1
162          #set topicInfo($chan,needVMode) 0
163          #set topicInfo($chan,Tflag) 1
164          set topicInfo($chan,canFlags) "o|ov"
165          set topicInfo($chan,canModes) "ov"
166          set topicInfo($chan,cantFlags) "T"
167          set topicInfo($chan,topicBits) [list]
168          set topicInfo($chan,learnOnChange) 1
169          set topicInfo($chan,initialised) "1"
170         
171          #DO NOT CHANGE THESE
172          set topicInfo($chan,topic) ""
173          set topicInfo($chan,whoSet) [list]
174          set topicInfo($chan,whenSet) [list]
175          set topicInfo($chan,whenLastSet) 0
176          set topicInfo($chan,whoLastSet) 0
177          set topicInfo($chan,lock) [list 0 "" ""]
178          set hasError 1
179        }
180      }
181    }
182  }
183  if {($topicEngineOnline == 0) || ($hasError == 1)} {
184    # now load the user defaults
185    # you should edit this file to give each channel the settings you want
186    global topicConfigPath botnet-nick
187    catch {
188      set topicEngineLoad 1
189      source "${topicConfigPath}/TopicEngineSettings.tcl"
190      if {[info exists botnet-nick]} {
191        #load a bot-specific file
192        source "${topicConfigPath}/TopicEngineSettings_${botnet-nick}.tcl"
193      }
194    }   
195  }
196}
197
198#### You won't need to change anything below this point ###
199
200#####
201# checkTopic
202#
203# Tries out a new topic (=current topic | newbit)
204# returns 0 if it'll fit in the network topic length limit
205# else returns the number of characters over the limit
206# allows for the " | " between topic elements
207#
208proc checkTopic {channel newbit {noOldTopic 0}} {
209  global topicLengthLimit topicInfo topicSeparator
210
211  #append the new bit onto the topic list
212  if {$noOldTopic == 0} {
213    set thisTopic $topicInfo($channel,topicBits)
214  } else {
215    #don't count the old topic, so initalise the 'old' one
216    set thisTopic [list]
217  }
218
219  if {$newbit != ""} { lappend thisTopic $newbit }
220 
221  if {$topicInfo($channel,leadIn) != ""} {
222    set thisTopic [linsert $thisTopic 0 $topicInfo($channel,leadIn)]
223  }
224
225  if {$topicInfo($channel,leadOut) != ""} {
226    set thisTopic [linsert $thisTopic end $topicInfo($channel,leadOut)]
227  }
228
229  set topicString ""
230
231  foreach bit $thisTopic {
232    append topicString $bit
233    append topicString " $topicSeparator "
234  }
235
236  set topicString [string range $topicString 0 [expr [string length $topicString] - 4 ]]
237
238  set topicLength [string length $topicString]
239
240  if {$topicLength > $topicLengthLimit} { return [expr $topicLength - $topicLengthLimit] }
241  return 0
242}
243
244#####
245# backupTopic {channel}
246#
247# saves the topic to another array so it can be recovered if needed
248#
249proc backupTopic { channel } {
250  global topicInfo backupInfo
251
252  set backupInfo($channel,topicBits) $topicInfo($channel,topicBits)
253  set backupInfo($channel,topic) $topicInfo($channel,topic)
254  set backupInfo($channel,whoSet) $topicInfo($channel,whoSet)
255  set backupInfo($channel,whenSet) $topicInfo($channel,whenSet)
256  set backupInfo($channel,whoLastSet) $topicInfo($channel,whoLastSet)
257  set backupInfo($channel,whenLastSet) $topicInfo($channel,whenLastSet)
258
259  return 0
260}
261
262#####
263# setTopic {channel, force = 0}
264#
265# sets the topic based on the topicBits list
266# will not change the topic if doesn't need it, unless force is 1
267#
268proc setTopic {channel {force 0}} {
269  global topicInfo loops topicSeparator bufferDirty topicEngineOnline
270
271  putloglev d * "topicengine: updating topic for $channel (force = $force)"
272
273  #debug for recursive stuff
274  set loops 0
275
276  #if we're not opped, don't even bother
277  if {![botisop $channel]} {
278    putlog "topicengine: er, I'm not opped in $channel, so I can't set the topic :("
279    return 0
280  }
281
282  set thisTopic $topicInfo($channel,topicBits) 
283 
284  ##leadin and leadout
285  if {$topicInfo($channel,leadIn) != ""} {
286    putloglev 1 * "topicengine: adding prefix"
287    set thisTopic [linsert $thisTopic 0 $topicInfo($channel,leadIn)]
288  }
289
290  if {$topicInfo($channel,leadOut) != ""} {
291    putloglev 1 * "topicengine: adding postfix"
292    set thisTopic [linsert $thisTopic end $topicInfo($channel,leadOut)]
293  }
294
295  ##build topic string
296  set topicString ""
297
298  foreach bit $thisTopic {
299    append topicString $bit
300    append topicString " $topicSeparator "
301  }
302
303  set topicString [string range $topicString 0 [expr [string length $topicString] - 4 ]]
304
305  #interpolate time stuff
306  set loops 0
307  while {[regexp "_TIME\{(.+?)\}" $topicString matches timeformat]} {
308    incr loops
309    if {$loops > 10} {
310      set line ""
311      putlog "topicengine: TREMENDOUS FAILURE! Topic for $channel couldn't be generated."
312      return 1
313    }
314    set timeString [clock format [clock seconds] -format $timeformat]
315    regsub -all "_TIME{$timeformat}" $topicString $timeString topicString
316  }
317
318  #putlog "topicengine: final topic for $channel is $topicString"
319
320  set topicInfo($channel,topic) $topicString
321  if {([topic $channel] == $topicString) && ($force == 0)} { return 0 }
322  if {$force == -1} { 
323    #just update the topic cache
324    return 0 
325  }
326 
327  putserv "TOPIC $channel :$topicString"
328  set bufferDirty 0
329  return 0
330}
331
332
333#####
334# topicChanged
335#
336# called when the topic is changed on a channel
337# checks if the topic is locked and changes it back if it needs to
338# if topicInfo(channel,learnOnChange) is 1, will parse this topic and learn it
339#
340proc topicChanged {nick host handle channel text} {
341  global topicInfo topicChannels
342 
343  #check the topic script is active in here
344  if {![channel get $channel topicengine]} {
345    return 0
346  }
347
348  #this is because the array isn't initialised when the bot first starts
349  topicInitArray 1
350
351  #if it's me, drop
352  if [isbotnick $nick] { return 0 }
353
354  #if it's the same topic as before, drop
355  if {$text == $topicInfo($channel,topic)} { return 0 }
356
357  #if it's an empty topic, redo it and don't learn it
358  if {$text == ""} {
359    setTopic $channel
360    return 0
361  }
362
363  #if it's a bot setting the topic, ignore it
364  if [matchattr $handle b] { return 0 }
365 
366  if {[lindex $topicInfo($channel,lock) 0] == 0} { 
367    #learn it?
368    if {$topicInfo($channel,learnOnChange) == 1} {
369      putlog "Topic in $channel changed, learning it"
370      #back it up
371      backupTopic $channel
372      topicParse $channel $text $nick
373    }
374    return 0 
375  }
376
377  #it's locked, put it back and notify
378  setTopic $channel 1
379  set whoLocked [lindex $topicInfo($channel,lock) 1]
380  set whenLocked [clock format [lindex $topicInfo($channel,lock) 2]]
381  putserv "NOTICE $nick :Sorry, the topic for $channel was locked by $whoLocked on $whenLocked."
382  putlog "Bouncing topic in $channel"
383  return 0
384}
385
386#####
387# topicCommand
388#
389# interact with the user, accepts the !topic... commands
390#
391proc topicCommand {nick host handle channel text {silent 0} } {
392  global topicInfo topicChannels loops bufferDirty
393  set doBuffer 0
394  #putlog "topic command: $text ($channel)"
395
396  #check the topic script is active in here
397  if {![channel get $channel topicengine]} {
398    return 0
399  }
400
401  #check I'm opped there
402  if {![botisop $channel]} {
403    topicNotice "I'm not opped in $channel; I can't manage the topic."
404    return 0
405  }
406
407  #lowercase the channel (for case insensitivity in the array)
408  set channel [string tolower $channel]
409
410  set text [string trim $text]
411
412  incr loops
413  if {$loops > 5} { 
414    set loops 0
415    putlog "TopicEngine internal error: recursive looping. Aborting processing of this command."
416    return 0
417  }
418
419  #this is because the array isn't initialised when the bot first starts
420  topicInitArray 1
421 
422  #these commands can be used by anyone
423################# INFO
424  if [regexp -nocase "^info ?(.+)?" $text boom param] {
425    set loops 0
426    if {$param == ""} { 
427      set updateTime [clock format $topicInfo($channel,whenLastSet)]
428      if {[lindex $topicInfo($channel,lock) 0] != 0} {
429        set whoLocked [lindex $topicInfo($channel,lock) 1]
430        set whenLocked [clock format [lindex $topicInfo($channel,lock) 2]]
431        set lockedString " ... locked \002\[\002 $whoLocked | $whenLocked \002\]\002"
432      } else {
433        set lockedString ""
434      }
435
436      set undoString " ... undo \002\[\002 no \002\]\002"
437
438      catch {
439        global backupInfo
440        if {$backupInfo($channel,topicBits) != ""} {
441          set undoString " ... undo \002\[\002 yes \002\]\002"
442        }
443      }
444
445      if {$bufferDirty == 1} {
446        set bufferString " ... buffer \002\[\002 dirty \002\]\002"
447      } else {
448        set bufferString ""
449      }
450
451      global topicInfoBroadcast topicType
452      if {$topicInfoBroadcast == 0} {
453        topicNotice "$channel: topic \002\[\002 $topicInfo($channel,topic) \002\]\002 ... changed \002\[\002 $topicInfo($channel,whoLastSet) | $updateTime \002\]\002${lockedString}${bufferString}${undoString}"
454      } else {
455        if {$topicType == "pub"} {
456          putserv "PRIVMSG $channel :$channel: topic \002\[\002 $topicInfo($channel,topic) \002\]\002 ... changed \002\[\002 $topicInfo($channel,whoLastSet) | $updateTime \002\]\002${lockedString}${bufferString}${undoString}"
457        } else {
458          #msg and dcc
459          topicNotice "$channel: topic \002\[\002 $topicInfo($channel,topic) \002\]\002 ... changed \002\[\002 $topicInfo($channel,whoLastSet) | $updateTime \002\]\002${lockedString}${bufferString}${undoString}"
460        }
461      }
462
463      return 1
464    }
465
466    if {$param == "undo"} {
467      set undoString "\002\[\002 unavailable \002\]\002"
468
469      catch {
470        global backupInfo topicType
471        if {$backupInfo($channel,topicBits) != ""} {
472          set undoString "\002\[\002 $backupInfo($channel,topic) \002\]\002"
473        }
474      }
475
476      if {$topicType == "pub"} {
477        putserv "PRIVMSG $channel :$channel: topic undo $undoString"
478      } else {
479        #msg and dcc
480        topicNotice "$channel: topic undo $undoString"
481      }
482
483      return 1
484    }
485
486    set elementCount [llength $topicInfo($channel,topicBits)]
487
488    if {$param < 1} {
489      topicNotice "Error: topic index too low!"
490      return 1
491    }
492
493    if {$param > $elementCount} {
494      topicNotice "Error: topic index too high!"
495      return 1
496    }
497
498    set actparam [expr $param - 1]
499    set updateTime [clock format [lindex $topicInfo($channel,whenSet) $actparam]]
500    global topicInfoBroadcast topicType
501    if {$topicInfoBroadcast == 0} {
502      topicNotice "$channel: element \002\[\002 $param = [lindex $topicInfo($channel,topicBits) $actparam] \002\]\002 ... set by \002\[\002 [lindex $topicInfo($channel,whoSet) $actparam] | $updateTime \002\]\002"
503    } else {
504        if {$topicType == "pub"} {
505          putserv "PRIVMSG $channel :$channel: element \002\[\002 $param = [lindex $topicInfo($channel,topicBits) $actparam] \002\]\002 ... set by \002\[\002 [lindex $topicInfo($channel,whoSet) $actparam] | $updateTime \002\]\002"
506        } else {
507          #msg and dcc
508          topicNotice "$channel: element \002\[\002 $param = [lindex $topicInfo($channel,topicBits) $actparam] \002\]\002 ... set by \002\[\002 [lindex $topicInfo($channel,whoSet) $actparam] | $updateTime \002\]\002"
509        }
510      }
511
512    return 1
513  }
514
515################# HELP
516  if [regexp -nocase "^help" $text] {
517    global botnick
518    topicNotice "Please use \002/msg $botnick topic help\002 for a command list"
519    return 1
520  }
521
522################# VERSION
523  if [string match -nocase "version" $text] {
524    topicNotice "I am running JamesOff's TopicEngine version $topicInfo(pop,version)."
525    return 1
526  }
527
528  #these commands need permissions
529  set canTopic 0
530 
531  #first we need to check global O and V
532  #if {($topicInfo($channel,needOGlobal) == 1) && [matchattr $handle o]} { set canTopic 1 }
533  #if {($topicInfo($channel,needVGlobal) == 1) && [matchattr $handle v]} { set canTopic 1 }
534
535  #now local O and V
536  #if {($topicInfo($channel,needOChan) == 1) && [matchattr $handle -|o $channel]} { set canTopic 1 }
537  #if {($topicInfo($channel,needVChan) == 1) && [matchattr $handle -|v $channel]} { set canTopic 1 }
538
539  #now modes in the channel
540  #if {($topicInfo($channel,needOMode) == 1) && [isop $nick $channel]} { set canTopic 1 }
541  #if {($topicInfo($channel,needVMode) == 1) && [isvoice $nick $channel]} { set canTopic 1 }
542
543  #now finally the T flag... this is different - if you HAVE it you can't set a topic
544  #Hack by Artoo; users with T flag can change topic, users with U flag can not
545  #if {($topicInfo($channel,Tflag) == 1) && [matchattr $handle T]} { set canTopic 1 }
546  #if {($topicInfo($channel,Tflag) == 1) && [matchattr $handle -|U $channel]} { set canTopic 0 }
547  #if {($topicInfo($channel,Tflag) == 1) && [matchattr $handle U]} { set canTopic 0 }
548  #if {($topicInfo($channel,Tflag) == 1) && [matchattr $handle -|T $channel]} { set canTopic 1 }
549
550  #New system: using channel settings
551 
552  # First we check canFlags, if the user has these eggdrop flags we'll allow use
553  if [matchattr $handle $topicInfo($channel,canFlags) $channel] {
554    putloglev d * "topicEngine: user $handle matches flags"
555    set canTopic 1
556  }
557
558  # Now check canModes, if the user has one of these modes we'll allow use
559  set modesList [split $topicInfo($channel,canModes) {}]
560  foreach modeChar $modesList {
561    if {($modeChar == "o") && [isop $nick $channel]} {
562      putloglev d * "topicEngine: user $nick is an op, allowed"
563      set canTopic 1
564      break
565    }
566
567    if {($modeChar == "v") && [isvoice $nick $channel]} {
568      putloglev d * "topicEngine: user $nick is a voice, allowed"
569      set canTopic 1
570      break
571    }
572
573    if {($modeChar == "h") && [ishalfop $nick $channel]} {
574      putloglev d * "topicEngine: user $nick is a halfop, allowed"
575      set canTopic 1
576      break
577    }
578  }
579
580  # Finally, if a user matches cantFlags, we'll disable the script for them (regardless of
581  # any other match so far
582
583  if [matchattr $handle $topicInfo($channel,cantFlags) $channel] {
584    putloglev d * "topicEngine: user $handle matches flags, disallowed"
585    set canTopic 0
586  }
587
588  if {$canTopic == 0} {
589    topicNotice "Sorry, you cannot use the !topic commands"
590    return 1
591  }
592
593################# UNDO
594  if [regexp -nocase "^undo(.+)?" $text boom params] {
595    if {([lindex $topicInfo($channel,lock) 0] != 0) && (![matchattr $handle |n $channel])} {
596      set whoLocked [lindex $topicInfo($channel,lock) 1]
597      topicNotice "Sorry, the topic has been locked by $whoLocked."
598      return 1
599    }
600    set loops 0
601
602    set params [string trim $params]
603
604    if {([string first "~" $params] == 0) || [string match -nocase "buffer" $params]} {
605      #buffer the topic
606      set bufferDirty 1
607      set doBuffer 1
608    }
609   
610    global backupInfo
611
612    if {$backupInfo($channel,topicBits) == ""} {
613      topicNotice "Sorry, no undo is available for $channel"
614      return 2
615    }
616
617    set topicInfo($channel,topicBits) $backupInfo($channel,topicBits)
618    set topicInfo($channel,whoSet) $backupInfo($channel,whoSet)
619    set topicInfo($channel,whenSet) $backupInfo($channel,whenSet)
620    set topicInfo($channel,whoLastSet) "$backupInfo($channel,whoLastSet)/undo:$nick"
621    set topicInfo($channel,whenLastSet) [clock seconds]
622
623    set backupInfo($channel,topicBits) ""
624    set backupInfo($channel,topic) ""
625
626    if {$doBuffer == 0} {
627      setTopic $channel
628    } else {
629      setTopic $channel -1
630    }
631 
632    return 1
633  }
634
635################# ADD
636  if [regexp -nocase "^add (.+)" $text boom params] {
637    if {([lindex $topicInfo($channel,lock) 0] != 0) && (![matchattr $handle |n $channel])} {
638      set whoLocked [lindex $topicInfo($channel,lock) 1]
639      topicNotice "Sorry, the topic has been locked by $whoLocked."
640      return 1
641    }
642    set loops 0
643    if {[string first "@" $params] == 0} {
644      #lock the topic
645      topicCommand $nick $host $handle $channel "lock"
646      set params [string range $params 1 end]
647    }
648    if {[string first "~" $params] == 0} {
649      #buffer the topic
650      set bufferDirty 1
651      set doBuffer 1
652      set params [string range $params 1 end]
653    }
654
655    #remove extra spaces
656    set params [string trim $params]
657    regsub -all "  +" $params " " params
658   
659    #check length
660    set tooMany [checkTopic $channel $params]
661    if {$tooMany} {
662      topicNotice "Sorry, adding that to the topic would make it go over the length limit by $tooMany characters. Please try a shorter topic, or the 'append' (<<<) command instead of 'add' (+)"
663      return 1
664    }
665
666    backupTopic $channel
667
668    lappend topicInfo($channel,topicBits) $params
669    lappend topicInfo($channel,whoSet) $nick
670    lappend topicInfo($channel,whenSet) [clock seconds]
671    set topicInfo($channel,whoLastSet) $nick
672    set topicInfo($channel,whenLastSet) [clock seconds]
673    if {$doBuffer == 0} {
674      setTopic $channel
675    } else {
676      setTopic $channel -1
677    }
678    return 1
679  }
680
681################# APPEND
682  if [regexp -nocase "^append (.+)" $text boom params] {
683    if {([lindex $topicInfo($channel,lock) 0] != 0) && (![matchattr $handle |n $channel])} {
684      set whoLocked [lindex $topicInfo($channel,lock) 1]
685      topicNotice "Sorry, the topic has been locked by $whoLocked."
686      return 1
687    }
688    set loops 0
689    if {[string first "@" $params] == 0} {
690      #lock the topic
691      topicCommand $nick $host $handle $channel "lock"
692      set params [string range $params 1 end]
693    }
694    if {[string first "~" $params] == 0} {
695      #buffer the topic
696      set bufferDirty 1
697      set doBuffer 1
698      set params [string range $params 1 end]
699    }
700
701    #remove extra spaces
702    set params [string trim $params]
703    regsub -all "  +" $params " " params
704   
705    #check length
706    set tooMany [checkTopic $channel $params]
707    set originalTopicBits $topicInfo($channel,topicBits)
708    set originalTopicWho $topicInfo($channel,whoSet)
709    set originalTopicWhen $topicInfo($channel,whenSet)
710    set count 0
711    while {$tooMany} {
712
713      #delete element 1 (return 2 = failed to delete)
714      set result [topicCommand $nick $host $handle $channel "del ~1" 1]
715
716      if {$result == 2} {
717        #wahey
718        topicNotice "Sorry, couldn't fit that in the topic. Please try something shorter"
719        set topicInfo($channel,topicBits) $originalTopicBits
720        set topicInfo($channel,topicWho) $originalTopicWho
721        set topicInfo($channel,topicWhen) $originalTopicWhen
722        return 2
723      }
724     
725      set tooMany [checkTopic $channel $params]
726      incr count
727      if {$count == 100} {
728        puthelp "ALERT: Looping too much in TopicEngine (append)"
729        return 0
730      }
731    }
732
733    backupTopic $channel
734
735    lappend topicInfo($channel,topicBits) $params
736    lappend topicInfo($channel,whoSet) $nick
737    lappend topicInfo($channel,whenSet) [clock seconds]
738    set topicInfo($channel,whoLastSet) $nick
739    set topicInfo($channel,whenLastSet) [clock seconds]
740    if {$doBuffer == 0} {
741      setTopic $channel
742    } else {
743      setTopic $channel -1
744    }
745    return 1
746  }
747
748################# INSERT
749  if [regexp -nocase "^insert (.+)" $text boom params] {
750    if {([lindex $topicInfo($channel,lock) 0] != 0) && (![matchattr $handle |n $channel])} {
751      set whoLocked [lindex $topicInfo($channel,lock) 1]
752      topicNotice "Sorry, the topic has been locked by $whoLocked."
753      return 1
754    }
755    set loops 0
756    if {[string first "@" $params] == 0} {
757      #lock the topic
758      topicCommand $nick $host $handle $channel "lock"
759      set params [string range $params 1 end]
760    }
761    if {[string first "~" $params] == 0} {
762      #buffer the topic
763      set bufferDirty 1
764      set doBuffer 1
765      set params [string range $params 1 end]
766    }
767
768    #remove extra spaces
769    set params [string trim $params]
770    regsub -all "  +" $params " " params
771   
772    #check length
773    set tooMany [checkTopic $channel $params]
774    set originalTopicBits $topicInfo($channel,topicBits)
775    set originalTopicWho $topicInfo($channel,whoSet)
776    set originalTopicWhen $topicInfo($channel,whenSet)
777    set count 0
778    while {$tooMany} {
779
780      #delete last element (return 2 = failed to delete)
781      set lastElement [llength $topicInfo($channel,topicBits)]
782      set result [topicCommand $nick $host $handle $channel "del ~$lastElement" 1]
783
784      if {$result == 2} {
785        #wahey
786        topicNotice "Sorry, couldn't fit that in the topic. Please try something shorter"
787        set topicInfo($channel,topicBits) $originalTopicBits
788        set topicInfo($channel,topicWho) $originalTopicWho
789        set topicInfo($channel,topicWhen) $originalTopicWhen
790        return 2
791      }
792     
793      set tooMany [checkTopic $channel $params]
794      incr count
795      if {$count == 100} {
796        puthelp "ALERT: Looping too much in TopicEngine (insert)"
797        return 0
798      }
799    }
800
801    backupTopic $channel
802
803    set topicInfo($channel,topicBits) [linsert $topicInfo($channel,topicBits) 0 $params]
804    set topicInfo($channel,whoSet) [linsert $topicInfo($channel,whoSet) 0 $nick]
805    set topicInfo($channel,whenSet) [linsert $topicInfo($channel,whenSet) 0 [clock seconds]]
806    set topicInfo($channel,whoLastSet) $nick
807    set topicInfo($channel,whenLastSet) [clock seconds]
808    if {$doBuffer == 0} {
809      setTopic $channel
810    } else {
811      setTopic $channel -1
812    }
813    return 1
814  }
815
816################# SET
817  if [regexp -nocase "^set (.+)" $text pop cmdString] {
818    if {([lindex $topicInfo($channel,lock) 0] != 0) && (![matchattr $handle |n $channel])} {
819      set whoLocked [lindex $topicInfo($channel,lock) 1]
820      topicNotice "Sorry, the topic has been locked by $whoLocked."
821      return 1
822    }
823    set loops 0
824    if [regexp -nocase "^(pre|post)fix (.+)" $cmdString boom which what] {
825      set which [string tolower $which]
826      if {$what == "none"} { set what "" }
827
828      if {$which == "pre"} {
829        set tooMany [checkTopic $channel $what]
830        if {$tooMany} {
831          topicNotice "Sorry, adding that to the topic would make it go over the length limit by $tooMany characters. Please try a shorter topic."
832          return 1
833        }
834        set topicInfo($channel,leadIn) $what     
835        setTopic $channel
836        return 1
837      }
838
839      if {$which == "post"} {
840        set tooMany [checkTopic $channel $what]
841        if {$tooMany} {
842          topicNotice "Sorry, adding that to the topic would make it go over the length limit by $tooMany characters. Please try a shorter topic."
843          return 1
844        }
845        set topicInfo($channel,leadOut) $what
846        setTopic $channel
847        return 1
848      }
849    }
850
851    #else set the topic as is
852    if {$cmdString == "none"} { 
853      set cmdString ""
854    }
855
856    if {[string first "@" $cmdString] == 0} {
857      #lock the topic
858      topicCommand $nick $host $handle $channel "lock"
859      set cmdString [string range $cmdString 1 end]
860    }
861
862    if {[string first "~" $cmdString] == 0} {
863      #buffer the topic
864      set bufferDirty 1
865      set doBuffer 1
866      set cmdString [string range $cmdString 1 end]
867    }
868
869
870    set tooMany [checkTopic $channel $cmdString 1]
871    if {$tooMany} {
872      topicNotice "Sorry, adding that to the topic would make it go over the length limit by $tooMany characters. Please try a shorter topic."
873      return 1
874    }
875
876    #remove extra spaces
877    set cmdString [string trim $cmdString]
878    regsub -all "  +" $cmdString " " cmdString
879   
880    backupTopic $channel
881
882    set topicInfo($channel,whoSet) [list $nick]
883    set topicInfo($channel,whenSet) [list [clock seconds]]
884    set topicInfo($channel,whoLastSet) $nick
885    set topicInfo($channel,whenLastSet) [clock seconds]
886    set topicInfo($channel,topicBits) [list $cmdString]
887    if {$doBuffer == 0} {
888      setTopic $channel
889    } else {
890      setTopic $channel -1
891    }
892    return 1
893  }
894
895################# DEL
896  if [regexp -nocase "^del (.+)" $text boom param] {
897    if {([lindex $topicInfo($channel,lock) 0] != 0) && (![matchattr $handle |n $channel])} {
898      set whoLocked [lindex $topicInfo($channel,lock) 1]
899      topicNotice "Sorry, the topic has been locked by $whoLocked."
900      return 1
901    }
902    set loops 0
903
904    #update the cached version
905    setTopic $channel -1
906
907    if {[string first "~" $param] == 0} {
908      #buffer the topic
909      set bufferDirty 1
910      set doBuffer 1
911      set param [string range $param 1 end]
912    }
913
914    set elementCount [llength $topicInfo($channel,topicBits)]
915
916    if {$param < 1} {
917      if {$silent == 0} {
918      topicNotice "Error: topic index too low!"
919      }
920      return 1
921    }
922
923    if {$param > $elementCount} {
924      if {$silent == 0} {
925      topicNotice "Error: topic index too high!"
926      }
927      return 2
928    }
929
930    backupTopic $channel
931
932    set param [expr $param - 1]
933    set topicInfo($channel,topicBits) [lreplace $topicInfo($channel,topicBits) $param $param]
934    set topicInfo($channel,whoSet) [lreplace $topicInfo($channel,whoSet) $param $param]
935    set topicInfo($channel,whenSet) [lreplace $topicInfo($channel,whenSet) $param $param]
936    set topicInfo($channel,whoLastSet) $nick
937    set topicInfo($channel,whenLastSet) [clock seconds]
938    if {$doBuffer == 0} {
939      setTopic $channel
940    } else {
941      setTopic $channel -1
942    }
943    return 1
944  }
945
946################# REGEXP
947  if [regexp -nocase {^regexp ([^ ]+) (.+)} $text matches param re] {
948    if {([lindex $topicInfo($channel,lock) 0] != 0) && (![matchattr $handle |n $channel])} {
949      set whoLocked [lindex $topicInfo($channel,lock) 1]
950      topicNotice "Sorry, the topic has been locked by $whoLocked."
951      return 1
952    }
953    set loops 0
954
955    #update the cached version
956    setTopic $channel -1
957
958    if {[string first "~" $re] == 0} {
959      #buffer the topic
960      set bufferDirty 1
961      set doBuffer 1
962      set param [string range $param 1 end]
963    }
964
965    set elementCount [llength $topicInfo($channel,topicBits)]
966
967    if {$param < 1} {
968      if {$silent == 0} {
969      topicNotice "Error: topic index too low!"
970      }
971      return 1
972    }
973
974    if {$param > $elementCount} {
975      if {$silent == 0} {
976      topicNotice "Error: topic index too high!"
977      }
978      return 2
979    }
980
981    if {![regexp {/(.+)/([^/]+)?/(.+)?} $re matches refirst resecond reopts]} {
982      topicNotice "Error: not a valid regexp. Use \002/match/replace/options\002."
983      return 2
984    }
985
986    set param [expr $param - 1]
987    set topicElement [lindex $topicInfo($channel,topicBits) $param]
988
989    set options ""
990    if [string match "*i*" $reopts] {
991      set options "-nocase"
992    }
993    if [string match "*g*" $reopts] {
994      append options "-all"
995    }
996
997    if {$options != ""} {
998      regsub $options $refirst $topicElement $resecond topicElement
999    } else {
1000      regsub $refirst $topicElement $resecond topicElement
1001    }
1002
1003    set oldTopic $topicInfo($channel,topicBits)
1004    set limit [checkTopic $channel ""]
1005    if {$limit > 0} {
1006      topicNotice "Sorry, that would make the topic go over the limit by $limit characters."
1007      set topicInfo($channel,topicBits) $oldTopic
1008      return 2
1009    }
1010
1011    backupTopic $channel
1012
1013    set topicInfo($channel,topicBits) [lreplace $topicInfo($channel,topicBits) $param $param $topicElement]
1014    set topicInfo($channel,whoSet) [lreplace $topicInfo($channel,whoSet) $param $param "$nick/regexp"]
1015    set topicInfo($channel,whenSet) [lreplace $topicInfo($channel,whenSet) $param $param [clock seconds]]
1016    set topicInfo($channel,whoLastSet) $nick
1017    set topicInfo($channel,whenLastSet) [clock seconds]
1018    if {$doBuffer == 0} {
1019      setTopic $channel
1020    } else {
1021      setTopic $channel -1
1022    }
1023    return 1
1024  }
1025
1026
1027
1028################# REHASH
1029  if [regexp -nocase "^(rehash|redo)( force)?" $text pop whee force] {
1030    set mustRedo 0
1031    if [string match -nocase " force" $force] {
1032      set mustRedo 1
1033    }
1034    setTopic $channel $mustRedo
1035    return 1
1036  }
1037
1038################# RESET/CLEAR
1039  if [regexp -nocase "^(clear|reset)( (content|all))?" $text pop blblbl whee opt] {
1040    if {([lindex $topicInfo($channel,lock) 0] != 0) && (![matchattr $handle |n $channel])} {
1041      set whoLocked [lindex $topicInfo($channel,lock) 1]
1042      topicNotice "Sorry, the topic has been locked by $whoLocked."
1043      return 1
1044    }
1045    set loops 0
1046    if {$opt == "all"} {
1047      set topicInfo($channel,leadIn) ""
1048      set topicInfo($channel,leadOut) ""
1049      set topicInfo($channel,topicBits) [list]
1050      set topicInfo($channel,whoSet) [list]
1051      set topicInfo($channel,whenSet) [list]
1052      setTopic $channel
1053      set topicInfo($channel,whoLastSet) $nick
1054      set topicInfo($channel,whenLastSet) [clock seconds]
1055      return 1
1056    }
1057
1058    if {$opt == "content"} {
1059      set topicInfo($channel,topicBits) [list]
1060      set topicInfo($channel,whoSet) [list]
1061      set topicInfo($channel,whenSet) [list]
1062      setTopic $channel
1063      set topicInfo($channel,whoLastSet) $nick
1064      set topicInfo($channel,whenLastSet) [clock seconds]
1065      return 1
1066    }
1067
1068    set topicInfo($channel,topic) ""
1069    if { [topic $channel] != ""} {
1070      putserv "TOPIC $channel :"
1071    }
1072
1073    return 1
1074  }
1075
1076################# LOCK
1077  if [string match -nocase "lock" $text] {
1078    if {![matchattr $handle |n $channel]} {
1079      topicNotice "Sorry, you cannot lock the topic."
1080      return 1
1081    }
1082    set loops 0
1083    if {[lindex $topicInfo($channel,lock) 0] == 1} {
1084      topicNotice "The topic is already locked."
1085      return 1
1086    }
1087
1088    set topicInfo($channel,lock) [list 1 $nick [clock seconds]]
1089    topicNotice "Locking topic for $channel."
1090    #check the topic is the cached one
1091    if {$topicInfo($channel,topic) != [topic $channel]} {
1092      setTopic $channel
1093    }
1094    # get any other bots that are locking this channel to unlock it
1095    putallbots "topicengine unlock $channel"
1096    return 1
1097  }
1098
1099################# UNLOCK
1100  if [string match -nocase "unlock" $text] {
1101    if {![matchattr $handle |n $channel]} {
1102      topicNotice "Sorry, you cannot unlock the topic."
1103      return 1
1104    }
1105    set loops 0
1106    if {[lindex $topicInfo($channel,lock) 0] == 0} {
1107      topicNotice "The topic is already unlocked."
1108      return 1
1109    }
1110
1111    set topicInfo($channel,lock) [list 0 "" ""]
1112    topicNotice "Unlocking topic in $channel."
1113    return 1
1114  }
1115
1116################# shortcuts
1117  if [regexp -nocase "^>>>(.+)" $text pop extra] {
1118    #append
1119    topicCommand $nick $host $handle $channel "insert $extra"
1120    return 1
1121  }
1122  if [regexp -nocase "^<<<(.+)" $text pop extra] {
1123    #append
1124    topicCommand $nick $host $handle $channel "append $extra"
1125    return 1
1126  }
1127  if [regexp -nocase "^\\\+(.+)" $text pop actual] {
1128    #add
1129    topicCommand $nick $host $handle $channel "add $actual"
1130    return 1
1131  }
1132  if [regexp -nocase "^\\\-(.+)" $text pop actual] {
1133    #del
1134    topicCommand $nick $host $handle $channel "del $actual"
1135    return 1
1136  }
1137  if [regexp -nocase "^\\\=(.+)" $text pop actual] {
1138    #set
1139    topicCommand $nick $host $handle $channel "set $actual"
1140    return 1
1141  }
1142  if [regexp -nocase "^\\\?(.+)?" $text pop actual] {
1143    #info
1144    topicCommand $nick $host $handle $channel "info $actual"
1145    return 1
1146  }
1147  if [regexp -nocase "^#(!)?" $text pop extra] {
1148    #rehash
1149    if {$extra == "!"} { set extra "force" }
1150    topicCommand $nick $host $handle $channel "rehash $extra"
1151    return 1
1152  }
1153  if [regexp -nocase {^/([0-9]+)(.+)} $text matches index exp] {
1154    #regexp
1155    topicCommand $nick $host $handle $channel "regexp $index $exp"
1156    return 1
1157  }
1158
1159 
1160  ##If we got here, they used the command wrong
1161  # assume they meant set, and tell them how to get help
1162
1163  ##Just double-check it's not that they left off all the text entirely, in which case we'll tell them the topic, and how to get help.
1164  if {$text == ""} {
1165    topicCommand $nick $host $handle $channel "info"
1166    global botnick
1167    topicNotice "\[FYI\] For help on the !topic commands, please do \002/msg $botnick topic help\002."
1168    return 1
1169  }
1170   
1171  #topicCommand $nick $host $handle $channel "set $text"
1172
1173  global botnick
1174  topicNotice "\[FYI\] Incorrect use of !topic command. You probably meant !topic set <topic>. Do \002/msg $botnick topic help\002 for more information."
1175  return 0
1176}
1177
1178#####
1179# topicHelp
1180#
1181# respond to /msg botnick topic help ... requests
1182#
1183proc topicHelp {nick host handle arg} {
1184  if [regexp -nocase "^help( .+)?" $arg boom helpon] {
1185    global botnick topicInfo
1186
1187    set helpon [string tolower $helpon]
1188    if {$helpon == ""} {
1189      #command list
1190      puthelp "PRIVMSG $nick :\002TopicEngine Script v$topicInfo(pop,version)\002 by JamesOff (james@jamesoff.net) http://www.jamesoff.net/go/topicengine";
1191      puthelp "PRIVMSG $nick :\037Commands available\037: (all from channel as !topic ..., or in query as /msg $botnick topic ..., or on partyline as .topic ..."
1192      puthelp "PRIVMSG $nick :Use \037/msg $botnick topic help <command>\037 for more info)";
1193      puthelp "PRIVMSG $nick :  info    set     add"
1194      puthelp "PRIVMSG $nick :  del     rehash  clear"
1195      puthelp "PRIVMSG $nick :  lock    unlock  append"
1196      puthelp "PRIVMSG $nick :  insert  undo    regexp"
1197      return 0
1198    }
1199
1200    set helpon [string range $helpon 1 [string length $helpon]]
1201
1202    if {$helpon == "info"} {
1203      puthelp "PRIVMSG $nick :\002!topic info\002"
1204      puthelp "PRIVMSG $nick :  This gives you summary information about what the topic is, and when it was last changed by whom"
1205      puthelp "PRIVMSG $nick :\002!topic info <n>\002"
1206      puthelp "PRIVMSG $nick :  This tells you about component n of the topic. The first component is 1. Be careful, the pre- and postfixes are not included in this."
1207      puthelp "PRIVMSG $nick :\002!topic info undo\002"
1208      puthelp "PRIVMSG $nick :  This tells you the status of the undo buffer for the channel. (See !topic undo)"
1209      puthelp "PRIVMSG $nick :\002!topic ?\002 and \002!topic ?<n>\002 are shortcuts for this command"
1210      return 0
1211    }
1212
1213    if {$helpon == "set"} {
1214      puthelp "PRIVMSG $nick :\002!topic set <string>"
1215      puthelp "PRIVMSG $nick :  This will remove all components of the topic (except the pre- and postfixes) and replace them with your string."
1216      puthelp "PRIVMSG $nick :  <string> can also be 'none' to clear the topic"
1217      puthelp "PRIVMSG $nick :\002!topic set prefix|postfix <string>"
1218      puthelp "PRIVMSG $nick :  This sets the prefix or the postfix to the string you give"
1219      puthelp "PRIVMSG $nick :  Use 'none' to clear them"
1220      puthelp "PRIVMSG $nick :\002!topic =<string>\002 and \002!topic =prefix|postfix <string>\002 are shortcuts for this command"
1221      puthelp "PRIVMSG $nick :You can prefix <string> with @ to make the bot lock the topic at the same time. (Not for pre/postfix)"
1222      return 0
1223    }
1224
1225    if {$helpon == "add"} {
1226      puthelp "PRIVMSG $nick :\002!topic add <string>"
1227      puthelp "PRIVMSG $nick :  Adds your string to the topic. Will fail if your string would make the topic go over the topic length limit."
1228      puthelp "PRIVMSG $nick :\002!topic +<string>\002 is a shortcut for this command"
1229      puthelp "PRIVMSG $nick :You can prefix <string> with @ to make the bot lock the topic at the same time."
1230      return 0
1231    }
1232
1233    if {$helpon == "append"} {
1234      puthelp "PRIVMSG $nick :\002!topic append <string>"
1235      puthelp "PRIVMSG $nick :  Adds your string to the topic. Will automatically drop elements from the start of the topic to try to fit your text in."
1236      puthelp "PRIVMSG $nick :\002!topic <<<<string>\002 is a shortcut for this command (e.g. !topic <<<hello)."
1237      puthelp "PRIVMSG $nick :You can prefix <string> with @ to make the bot lock the topic at the same time."
1238      return 0
1239    }
1240
1241    if {$helpon == "append"} {
1242      puthelp "PRIVMSG $nick :\002!topic insert <string>"
1243      puthelp "PRIVMSG $nick :  Adds your string to the front of the topic. Will automatically drop elements from the end of the topic to try to fit your text in."
1244      puthelp "PRIVMSG $nick :\002!topic >>><string>\002 is a shortcut for this command (e.g. !topic >>>hello)."
1245      puthelp "PRIVMSG $nick :You can prefix <string> with @ to make the bot lock the topic at the same time."
1246      return 0
1247    }
1248
1249    if {$helpon == "regexp"} {
1250      puthelp "PRIVMSG $nick :\002!topic regexp <index> <regular expression>"
1251      puthelp "PRIVMSG $nick :  Uses a regexp replace on the topic element at <index>."
1252      puthelp "PRIVMSG $nick :  The correct form for the regexp is: /match/replace/options"
1253      puthelp "PRIVMSG $nick :  Options is nothing, or a combination of \002i\002 for case-insensitive matching, and \002g\002 for global matching (match all occurances in string)."
1254      puthelp "PRIVMSG $nick :\002!topic /<index>/<regexp>/\002 is a shortcut for this command (e.g. !topic /2/hello/goodbye/)."
1255      puthelp "PRIVMSG $nick :Collected terms can be used in the replacement with \\1, \\2, etc (if you don't understand, this command may not be for you :) See \002man regexp\002 for more information."
1256      return 0
1257    }
1258
1259    if {$helpon == "del"} {
1260      puthelp "PRIVMSG $nick :\002!topic del <n>"
1261      puthelp "PRIVMSG $nick :  Deletes topic component n from the topic, first is 1. You cannot use this on the pre- or postfixes (see \037!topic set\037 for info)"
1262      puthelp "PRIVMSG $nick :\002!topic -<n>\002 is a shortcut for this command"
1263      return 0
1264    }
1265
1266    if {$helpon == "rehash"} {
1267      puthelp "PRIVMSG $nick :\002!topic rehash\002"
1268      puthelp "PRIVMSG $nick :  Forces the bot to reset the topic to what it thinks it should be. Will do nothing if the actual topic matches"
1269      puthelp "PRIVMSG $nick :  what the bot thinks it should be."
1270      puthelp "PRIVMSG $nick :\002!topic rehash force\002"
1271      puthelp "PRIVMSG $nick :  Forces the bot to reset to the topic, whether it thinks it should or not."
1272      puthelp "PRIVMSG $nick :  Note: redo is a synonym for rehash"
1273      return 0
1274    }
1275
1276    if {$helpon == "clear"} {
1277      puthelp "PRIVMSG $nick :\002!topic clear\002"
1278      puthelp "PRIVMSG $nick :  Sets the channel topic to nothing, but keeps the settings in the bot. Use \037!topic rehash\037 to get it back."
1279      puthelp "PRIVMSG $nick :\002!topic clear content"
1280      puthelp "PRIVMSG $nick :  Sets the content of the topic (not the pre- or postfix) to nothing"
1281      puthelp "PRIVMSG $nick :\002!topic clear all"
1282      puthelp "PRIVMSG $nick :  Sets all of the topic, include the pre- and postfixes, to nothing"
1283      puthelp "PRIVMSG $nick :Note: reset is a synonym for clear"
1284      return 0
1285    }
1286
1287    if {$helpon == "lock"} {
1288      puthelp "PRIVMSG $nick :\002!topic lock\002"
1289      puthelp "PRIVMSG $nick :  Locks the topic. The topic can still be changed using the !topic commands, but if anyone"
1290      puthelp "PRIVMSG $nick :  changes the topic manually (/topic) the bot will set it back. See also \037!topic unlock\037"
1291      puthelp "PRIVMSG $nick :You can prefix a topic with @ when using !topic set or !topic add to make the bot lock the topic at the same time."
1292    }
1293
1294    if {$helpon == "unlock"} {
1295      puthelp "PRIVMSG $nick :\002!topic unlock\002"
1296      puthelp "PRIVMSG $nick :  Unlocks the topic after it has been locked with \037!topic lock\037"
1297      return 0
1298    }
1299
1300    if {$helpon == "undo"} {
1301      puthelp "PRIVMSG $nick :\002!topic undo\002"
1302      puthelp "PRIVMSG $nick :  Restores the topic to its state before the last command. Currently only one level of undo is supported. Use \002!topic info undo\002 to see what the undo topic will be."
1303      return 0
1304    }
1305
1306  }
1307}
1308
1309
1310#####
1311# topicUnsplit
1312#
1313# called on a net-rejoin
1314# forces the topic to be set to what it should be, in case some servers around the network have lost it
1315#
1316proc topicUnsplit {nick host handle channel} {
1317  global topicInfo topicChannels topicLastSplit topicSplitChans topicSplitDirty
1318 
1319  #check the topic script is active in here
1320  if {![channel get $channel topicengine]} {
1321    return 0
1322  }
1323
1324        set topicSplitDirty 1
1325
1326        set topicLastSplit [clock seconds]
1327        lappend $topicSplitChans $channel
1328        set topicSplitChans [lsort -unique $topicSplitChans]
1329
1330        putloglev d * "topicengine: last split time updated to $topicLastSplit ([clock format $topicLastSplit])"
1331        putloglev d * "topicengine: dirty chans due to split: $topicSplitChans"
1332 
1333  return 0
1334}
1335
1336#####
1337# topicJoin
1338#
1339# (a misnomer) called when the bot is opped
1340# similar to the rejoin one above, checks the topic and resets it if needs to be
1341# if opped by a server, assume we've come back from a split, force the reset
1342#
1343proc topicJoin {nick host handle channel mode victim} {
1344  global topicInfo topicChannels
1345
1346  #only do this if I've joined a channel
1347  if [isbotnick $victim] {
1348     
1349    #check the topic script is active in here
1350    if {![channel get $channel topicengine]} {
1351      return 0
1352    }
1353
1354    #check i got opped
1355    if {$mode != "+o"} { return 0 }
1356    #first check i haven't reset the topic in here myself in the last 120 seconds (stop multiple rejoins fucking up)
1357    set thirtySecAgo [expr [clock seconds] - 120]
1358    if {($thirtySecAgo <= $topicInfo($channel,whenSet)) && ($topicInfo($channel,whoSet) == "me")} { return 0 }
1359    putlog "Opped in $channel, auto-setting topic"
1360    #if it's a server-mode change, assume we just got un-netsplitted, force the topic
1361    if {$nick == ""} {
1362      setTopic $channel 1
1363    } else {
1364      setTopic $channel
1365    }
1366    set topicInfo($channel,whoLastSet) "me"
1367    set topicInfo($channel,whenLastSet) [clock seconds]
1368  }
1369  return 0
1370}
1371
1372#####
1373# topicParse
1374#
1375# turns a string into a new set of topicBits
1376# used when learning a topic
1377#
1378proc topicParse {channel topic nick} {
1379  global topicInfo topicSeparator
1380
1381  #if we don't have a |, it's one topic
1382  if {[string first $topicSeparator $topic] == -1} {
1383    set topicInfo($channel,topicBits) [list $topic]
1384    putlog "Learned new topic $topic in $channel ... updating to check it has pre/postfixes for this channel"
1385    set topicInfo($channel,whoSet) [list $nick]
1386    set topicInfo($channel,whenSet) [list [clock seconds]]
1387    set topicInfo($channel,whoLastSet) $nick
1388    set topicInfo($channel,whenLastSet) [clock seconds]
1389    set topicInfo($channel,topic) $topic
1390    set willFit [checkTopic $channel ""]
1391    if {$willFit > 0} {
1392      putlog "Oops, I can't fit the new topic in with my pre/postfixes, not setting"
1393      return 0
1394    }
1395    setTopic $channel 0
1396    return 0
1397  }
1398
1399  #it's a multipart topic
1400  set topic "${topic}${topicSeparator}"
1401  set blah 0
1402  set loopCount 0
1403  set topicInfo($channel,topic) $topic
1404
1405  while {[string match "*$topicSeparator*" $topic]} {
1406    set sentence [string range $topic 0 [expr [string first $topicSeparator $topic] -1]]
1407    if {$sentence != ""} { 
1408      if {$blah == 0} {
1409        set topicInfo($channel,topicBits) [list [string trim $sentence]]
1410        set topicInfo($channel,whoSet) [list $nick]
1411        set topicInfo($channel,whenSet) [list [clock seconds]]
1412        set blah 1
1413      } else {
1414        lappend topicInfo($channel,topicBits) [string trim $sentence]
1415        lappend topicInfo($channel,whoSet) $nick
1416        lappend topicInfo($channel,whenSet) [clock seconds]
1417      }
1418    }
1419    set topic [string range $topic [expr [string first $topicSeparator $topic] +1] end]
1420    incr loopCount
1421    if {$loopCount > 10} { 
1422      putlog "Couldn't get all of the topic"
1423      return 0
1424    }
1425  }
1426  if {[lindex $topicInfo($channel,topicBits) 0] == $topicInfo($channel,leadIn)} {
1427    #the prefix is already on this topic, drop it
1428    putlog "Prefix on this topic is the same as the one I have on record, dropping it"
1429    set topicInfo($channel,topicBits) [lreplace $topicInfo($channel,topicBits) 0 0]
1430    set topicInfo($channel,whoSet) [lreplace $topicInfo($channel,whoSet) 0 0]
1431    set topicInfo($channel,whenSet) [lreplace $topicInfo($channel,whenSet) 0 0]
1432    incr loopCount -1
1433  }
1434
1435  set lastElement [expr $loopCount - 1]
1436  if {[lindex $topicInfo($channel,topicBits) $lastElement] == $topicInfo($channel,leadOut)} {
1437    #the prefix is already on this topic, drop it
1438    putlog "Postfix on this topic is the same as the one I have on record, dropping it"
1439    set topicInfo($channel,topicBits) [lreplace $topicInfo($channel,topicBits) $lastElement $lastElement]
1440    set topicInfo($channel,whoSet) [lreplace $topicInfo($channel,whoSet) $lastElement $lastElement]
1441    set topicInfo($channel,whenSet) [lreplace $topicInfo($channel,whenSet) $lastElement $lastElement]
1442  }
1443
1444  set topicInfo($channel,whoLastSet) $nick
1445  set topicInfo($channel,whenLastSet) [clock seconds]
1446  putlog "Learned topic with $loopCount elements in $channel ... updating to check it has pre/postfixes for this channel"
1447  set willFit [checkTopic $channel ""]
1448  if {$willFit > 0} {
1449    putlog "Oops, I can't fit the new topic in with my pre/postfixes, not setting"
1450    return 0
1451  }
1452  setTopic $channel 0
1453  return 0
1454}
1455
1456#####
1457# topicBotCommand
1458#
1459# handle a 'topicengine' command from another bot
1460#
1461proc topicBotCommand {fromBot cmd arg} {
1462  global topicInfo
1463  if {$cmd == "unlock"} {
1464    #need to unlock a channel
1465    if {[lindex $topicInfo($channel,lock) 0] == 1} {
1466      set topicInfo($arg,lock) [list 0 "" ""]
1467      putlog "Unlocked topic in $arg at request of $fromBot"
1468      putbot $fromBot "unlockok $arg"
1469    }
1470    return 0
1471  }
1472
1473  if {$cmd == "unlockok"} {
1474    #bot unlocked channel ok
1475    putlog "$fromBot unlocked channel in $arg for me"
1476    return 0
1477  }
1478}
1479
1480
1481#####
1482# topicUpdate
1483#
1484# update the topic automagically at 00:01 every day
1485#
1486proc topicUpdate { min hour day month year } { 
1487  global topicChannels
1488  putlog "topicEngine: auto-refreshing topics..."
1489
1490  foreach chan $topicChannels {
1491    setTopic $chan
1492  }
1493
1494  return 0
1495} 
1496
1497#####
1498# topicNotice
1499#
1500# Puts text back to the current executing user (puthelp or putidx as needed)
1501#
1502proc topicNotice { text } {
1503  global topicType topicParameter
1504
1505  #putlog "topicNotice: $text ($topicType = $topicParameter)"
1506
1507  if {$topicType == ""} {
1508    putlog "topicengine: CRITIAL ALERT: couldn't work out where to send $text"
1509    return 1
1510  }
1511
1512  if {$topicParameter == ""} {
1513    putlog "topicengine: CRITICAL ALERT: couldn't work out to whom I should send $text"
1514    return 1
1515  }
1516
1517  if {$topicType == "dcc"} {
1518    putidx $topicParameter $text
1519  } else {
1520    puthelp "NOTICE $topicParameter :$text"
1521  }
1522}
1523
1524#####
1525# topicCommandPub
1526#
1527# Wrapper for topicCommand from !topic in a channel
1528#
1529proc topicCommandPub {nick host handle channel text } {
1530  global topicType topicParameter
1531
1532  set topicType "pub"
1533  set topicParameter $nick
1534
1535  set result [topicCommand $nick $host $handle $channel $text]
1536
1537  set topicType ""
1538  set topicParameter ""
1539
1540  return $result
1541}
1542
1543#####
1544# topicCommandMsg
1545#
1546# Wrapper for topicCommand from topic in a msg
1547#
1548proc topicCommandMsg {nick host handle text} {
1549  global topicType topicParameter botnick
1550
1551  if [string match -nocase "help*" $text] {
1552    topicHelp $nick $host $handle $text
1553    return 0
1554  }
1555
1556  if [regexp -nocase {(#[^ ]+) (.+)} $text matches channel text2] {
1557
1558    set topicType "msg"
1559    set topicParameter $nick
1560
1561    set result [topicCommand "${nick}/msg" $host $handle $channel $text2]
1562
1563    set topicType ""
1564    set topicParameter ""
1565    return $result
1566  } else {
1567    puthelp "NOTICE $nick :use: /msg $botnick topic #channel ..."
1568  }
1569}
1570
1571#####
1572# topicCommandDCC
1573#
1574# Wrapper for topicCommand from .topic in DCC
1575#
1576proc topicCommandDCC {handle idx args} {
1577  global topicType topicParameter
1578
1579  if [regexp -nocase "(#.+) (.+)\}" $args matches channel text] {
1580
1581    set topicType "dcc"
1582    set topicParameter $idx
1583
1584    #putlog "calling topicCommand $channel $text"
1585
1586    set result [topicCommand "${handle}/dcc" "" $handle $channel $text]
1587
1588    set topicType ""
1589    set topicParameter ""
1590    return $result
1591  } else {
1592    putidx $idx "use: .topic #channel ..."
1593  }
1594}
1595
1596#####
1597# topicSplitCheck
1598# handle splits intelligently
1599#
1600proc topicSplitCheck {hr min day month year} {
1601        global topicLastSplit topicSplitChans topicSplitDirty
1602
1603        if {$topicSplitDirty == 0} {
1604                return
1605        }
1606
1607        set splitDiff [expr [clock seconds] - $topicLastSplit]
1608        putloglev 1 * "topicengine: checking for splits: diff between now and last split is $splitDiff sec"
1609       
1610        if {$splitDiff > 120} {
1611                # redo topic in every chan
1612                #don't do this is the setting is off
1613                putlog "topicengine: re-setting topics for channels involved in splits..."
1614                global topicAnnounceReset
1615                foreach channel $topicSplitChans {
1616                        if {$topicAnnounceReset == 1} {
1617                                puthelp "PRIVMSG $channel :Don't mind me, just resetting the topic in case a netsplit lost it :)"
1618                        }
1619                        setTopic $channel 1
1620                }
1621                set topicSplitChans [list]
1622                set topicSplitDirty 0
1623        }
1624}
1625
1626#####
1627# Start up stuff
1628#
1629# Initialise the variables at start
1630topicInitArray
1631#
1632# set up the binds
1633bind pub - !topic topicCommandPub
1634bind dcc - topic topicCommandDCC
1635bind topc - * topicChanged
1636bind msg - topic topicCommandMsg
1637#try to detect a net-unsplit and check the topic
1638bind rejn - * topicUnsplit
1639#set the topic to the cached one when I get opped
1640bind mode - * topicJoin
1641#auto unlock a channel if another bot locks it
1642bind bot - "topicengine" topicBotCommand
1643# update (change or comment out as needed) (syntax: min hour day month year)
1644bind time - "01 00 * * *" topicUpdate
1645
1646#check for topics needing resettings after a split
1647bind time - "* * * * *" topicSplitCheck
1648#
1649# log our existence
1650putlog "TopicEngine v$topicInfo(pop,version) online.";
1651# set the loaded variable so we don't overwrite topics on a .rehash
1652set topicEngineOnline 1
1653
1654# done :)
Note: See TracBrowser for help on using the repository browser.