TopicEngine.tcl 53 KB

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