TriviaEngine-sqlite.tcl 49 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999
  1. # The trivia engine YAY
  2. # vim: foldmethod=marker:foldcolumn=2:foldmarker=<<<,>>>:sw=2:ts=2
  3. ### INIT <<<
  4. package require sqlite
  5. # the channel to play in
  6. set trivia_channel "#triviacow"
  7. # the time between hints (sec)
  8. set trivia_speed 20
  9. # the time between rounds (sec)
  10. set trivia_delay 30
  11. # allow new categories to be created?
  12. set trivia_allow_new_cats 0
  13. # time before end of round to start counting down
  14. set trivia_time_left_warning 86400
  15. # hold the current question info <<<
  16. set trivia_q_id ""
  17. set trivia_q_cat ""
  18. set trivia_q_question ""
  19. set trivia_q_answer ""
  20. set trivia_q_hint ""
  21. set trivia_q_attempts 0
  22. set trivia_unanswered 0
  23. set trivia_run_last 0
  24. set trivia_run_qty 0
  25. set trivia_run_nick ""
  26. set trivia_guesser ""
  27. set trivia_guesser_count 0
  28. set trivia_last_qid 0
  29. set trivia_q_timestamp 0
  30. if [info exists trivia_must_rehash] {
  31. if {$trivia_must_rehash == 1} {
  32. set trivia_must_rehash 2
  33. } else {
  34. set trivia_must_rehash 0
  35. }
  36. } else {
  37. set trivia_must_rehash 0
  38. }
  39. #>>>
  40. # 0 = off
  41. # 1 = on
  42. # -1 = disabled
  43. set trivia_status 0
  44. set trivia_timer ""
  45. set trivia_watchdog_timer ""
  46. set trivia_last_ts 0
  47. set trivia_score_time 0
  48. set trivia_debug_mode 0
  49. set trivia_warned 0
  50. set trivia_asking_question 0
  51. set trivia_current_leader -1
  52. #colours <<<
  53. set trivia_c(off) "\003"
  54. set trivia_c(red) "\0034"
  55. set trivia_c(blue) "\0033"
  56. set trivia_c(purple) "\0036"
  57. set trivia_c(bold) "\002"
  58. set trivia_c(realblue) "\0032"
  59. #>>>
  60. bind pubm - * trivia_input
  61. bind msg - trivia trivia_msg
  62. # connect database <<<
  63. proc trivia_connect { } {
  64. global trivia_db_handle
  65. sqlite3 trivia_db_handle trivia.db
  66. }
  67. #>>>
  68. #>>>
  69. ### SETTINGS <<<
  70. set trivia_flag T
  71. set trivia_admin S
  72. #botnet handle of the bmotion bot we're going to play with (set blank to disable)
  73. set trivia_bmotion_bot "NoTopic"
  74. # >>>
  75. # Send a botnet command to the bmotion bot
  76. proc trivia_bmotion_send { command params } {
  77. global trivia_bmotion_bot
  78. if {$trivia_bmotion_bot == ""} {
  79. return 0
  80. }
  81. if [islinked $trivia_bmotion_bot] {
  82. putbot $trivia_bmotion_bot "trivia $command :$params"
  83. putloglev d * "triviaengine: send $command to $trivia_bmotion_bot with params $params"
  84. } else {
  85. putlog "triviaengine: trying to communicate with bmotion bot $trivia_bmotion_bot but it's not linked"
  86. }
  87. }
  88. # Handle a /msg
  89. proc trivia_msg { nick host handle cmd } {
  90. #<<<
  91. global trivia_db_handle trivia_last_qid
  92. global trivia_q_cat trivia_c trivia_allow_new_cats
  93. regsub "(trivia )" $cmd "" cmd
  94. putlog "trivia: msg command $cmd from $nick"
  95. if {($nick == "Greeneyez") || ($nick == "JamesOff")} {
  96. #<<< set a question's category
  97. if [regexp -nocase "^setcat (.+)" $cmd matches category] {
  98. if {$trivia_last_qid == 0} {
  99. puthelp "PRIVMSG $nick :Can't fathom last question, unable to update category"
  100. return
  101. }
  102. set orig_cat $category
  103. set category [trivia_sqlite_escape $category]
  104. set sql "SELECT cat_id FROM categories WHERE cat_name='$category'"
  105. set cat_id 0
  106. set cat_id [trivia_db_handle eval $sql]
  107. if {$cat_id != ""} {
  108. puthelp "PRIVMSG $nick :Moving question $trivia_last_qid to category$trivia_c(purple) $category$trivia_c(off) ($cat_id)"
  109. set sql "UPDATE questions SET cat_id='$cat_id' WHERE question_id='$trivia_last_qid'"
  110. trivia_db_handle eval $sql
  111. set trivia_q_cat $orig_cat
  112. return
  113. } else {
  114. if {$trivia_allow_new_cats == 1} {
  115. puthelp "PRIVMSG $nick :Creating new category$trivia_c(purple) $category"
  116. set sql "INSERT INTO categories VALUES (null, '$category', 1)"
  117. putloglev d * $sql
  118. trivia_db_handle eval $sql
  119. set cat_id [trivia_db_handle last_insert_rowid]
  120. puthelp "PRIVMSG $nick :Moving question $trivia_last_qid to category$trivia_c(purple) $category$trivia_c(off) ($cat_id)"
  121. set sql "UPDATE questions SET cat_id='$cat_id' WHERE question_id='$trivia_last_qid'"
  122. putloglev d * $sql
  123. trivia_db_handle eval $sql
  124. set trivia_q_cat $orig_cat
  125. } else {
  126. puthelp "PRIVMSG $nick :Creating new categories is $trivia_c(purple)DISABLED$trivia_c(off). ('trivia newcat enable' to enable.)"
  127. }
  128. return
  129. }
  130. }
  131. #>>>
  132. #<<< enable or disable new category creation
  133. if [regexp -nocase "^newcat (enable|disable)" $cmd matches toggle] {
  134. if {$toggle == "enable"} {
  135. puthelp "PRIVMSG $nick :$trivia_c(purple)ENABLING$trivia_c(off) new category creation"
  136. set trivia_allow_new_cats 1
  137. return
  138. }
  139. if {$toggle == "disable"} {
  140. puthelp "PRIVMSG $nick :$trivia_c(purple)DISABLING$trivia_c(off) new category creation"
  141. set trivia_allow_new_cats 0
  142. return
  143. }
  144. puthelp "PRIVMSG $nick :Use: trivia newcat enable|disable"
  145. return
  146. }
  147. #>>>
  148. #<<< debug
  149. if [regexp -nocase {^debug (.+)} $cmd matches func] {
  150. global trivia_debug_mode
  151. if {$trivia_debug_mode == 0} {
  152. putquick "PRIVMSG $nick :Debug mode is disabled. Cannot use debug commands."
  153. return 0
  154. }
  155. if {$func == "endweek"} {
  156. putquick "PRIVMSG $nick :Ending this week in 10 seconds..."
  157. global trivia_score_time
  158. set trivia_score_time [expr [clock seconds] + 10]
  159. return 0
  160. }
  161. }
  162. #>>>
  163. #<<< handle reports
  164. if [regexp -nocase {^report (help|list|fix|view|done|delete)?( .+)?} $cmd matches func arg] {
  165. if {($func == "") || ($func == "help")} {
  166. puthelp "PRIVMSG $nick :Use: report (list|view|fix|done|delete)"
  167. puthelp "PRIVMSG $nick :report list: see 10 reports"
  168. puthelp "PRIVMSG $nick :report view <id>: see the question and answer associated with a report"
  169. puthelp "PRIVMSG $nick :report fix <id> (question|answer) <new text>: update a question or answer"
  170. puthelp "PRIVMSG $nick :report done <id>: mark a report as done"
  171. return 0
  172. }
  173. if {$func == "list"} {
  174. putserv "PRIVMSG $nick :Gathering 10 trivia reports..."
  175. set sql "SELECT * FROM reports WHERE resolved = 'N' LIMIT 10"
  176. trivia_db_handle eval $sql result {
  177. putserv "PRIVMSG $nick :$result(report_id): [format %10s $result(who)] $result(message)"
  178. }
  179. return 0
  180. }
  181. if {$func == "view"} {
  182. if {$arg == ""} {
  183. puthelp "PRIVMSG $nick :Use: report view <id>"
  184. return 0
  185. }
  186. set arg [string trim $arg]
  187. set arg [trivia_sqlite_escape $arg]
  188. set sql "SELECT * FROM reports, questions WHERE report_id = '$arg' AND reports.question_id = questions.question_id"
  189. trivia_db_handle eval $sql result {
  190. putserv "PRIVMSG $nick :Report$trivia_c(purple) $result(report_id) $trivia_c(off)by$trivia_c(purple) $result(who) $trivia_c(off)on$trivia_c(blue) [clock format $result(when) -gmt 1]"
  191. putserv "PRIVMSG $nick :Question: $result(question)"
  192. putserv "PRIVMSG $nick : Answer: $result(answer)"
  193. putserv "PRIVMSG $nick : Report: $result(message)"
  194. }
  195. return 0
  196. }
  197. if {$func == "fix"} {
  198. set arg [string trim $arg]
  199. if [regexp -nocase {([0-9]+) (question|answer) (.+)} $arg matches report_id thing fix] {
  200. putserv "PRIVMSG $nick :Attempting to fix $trivia_c(purple)$thing$trivia_c(off) from report$trivia_c(purple) $report_id"
  201. #get question ID for this report
  202. set sql "SELECT question_id FROM reports WHERE report_id = '$report_id'"
  203. putloglev d * $sql
  204. set question_id 0
  205. trivia_db_handle eval $sql result {
  206. set question_id $result(question_id)
  207. }
  208. if {$question_id == 0} {
  209. putserv "PRIVMSG $nick :I can't seem to find that report, sorry :("
  210. return 0
  211. }
  212. set fix [trivia_sqlite_escape $fix]
  213. set sql "UPDATE questions SET $thing = '$fix' WHERE question_id = '$question_id'"
  214. putloglev d * $sql
  215. trivia_db_handle eval $sql
  216. putserv "PRIVMSG $nick :Updated!"
  217. return 0
  218. } else {
  219. puthelp "PRIVMSG $nick :Use: report fix <id> (question|answer) <new text>"
  220. return 0
  221. }
  222. }
  223. if {$func == "done"} {
  224. set arg [string trim $arg]
  225. set arg [trivia_sqlite_escape $arg]
  226. putserv "PRIVMSG $nick :Marking report$trivia_c(purple) $arg $trivia_c(off)as done."
  227. set sql "UPDATE reports SET resolved = 'Y' WHERE report_id = '$arg'"
  228. putloglev d * $sql
  229. trivia_db_handle eval $sql
  230. return 0
  231. }
  232. if {$func == "delete"} {
  233. set arg [string trim $arg]
  234. set arg [trivia_sqlite_escape $arg]
  235. putserv "PRIVMSG $nick :Deleting question from report$trivia_c(purple) $arg $trivia_c(off) and marking report as done."
  236. set sql "DELETE FROM questions WHERE question_id IN (SELECT question_id FROM reports WHERE report_id = '$arg')"
  237. putloglev d * $sql
  238. trivia_db_handle eval $sql
  239. set sql "UPDATE reports SET resolved = 'Y' WHERE report_id = '$arg'"
  240. putloglev d * $sql
  241. trivia_db_handle eval $sql
  242. return 0
  243. }
  244. }
  245. #>>>
  246. }
  247. }
  248. #>>>
  249. # Handle input coming from a channel
  250. proc trivia_input { nick host handle channel arg } {
  251. #<<<
  252. global trivia_channel trivia_status trivia_q_answer
  253. global trivia_guesser_count trivia_guesser trivia_c
  254. if {[string tolower $trivia_channel] != [string tolower $channel]} {
  255. return 0
  256. }
  257. if [regexp -nocase "^!t(rivia)? (.+)" $arg matches cmd param] {
  258. trivia_command $nick $host $handle $channel $param
  259. return 0
  260. }
  261. if [string match -nocase "!start" $arg] {
  262. if {$trivia_status > 0} {
  263. return
  264. }
  265. puthelp "PRIVMSG $trivia_channel :YOU'RE DOING IT WRONG"
  266. }
  267. if [regexp -nocase "^!t(rivia)?$" $arg] {
  268. if {$trivia_status == 1} {
  269. puthelp "PRIVMSG $trivia_channel :!trivia ... ?"
  270. return 0
  271. } else {
  272. puthelp "PRIVMSG $trivia_channel :Perhaps you want $trivia_c(purple)!trivia start"
  273. return 0
  274. }
  275. }
  276. # need to be running below here
  277. if {$trivia_status == -1} {
  278. return 0
  279. }
  280. if {$trivia_q_answer == ""} {
  281. return 0
  282. }
  283. #see if someone else is playing
  284. if {($nick == $trivia_guesser) && ($nick != "NoTopic")} {
  285. incr trivia_guesser_count
  286. putloglev d * "$nick has talked $trivia_guesser_count times in a row..."
  287. } else {
  288. set trivia_guesser_count 0
  289. set trivia_guesser $nick
  290. }
  291. #tidy the string up
  292. set arg [string tolower $arg]
  293. set arg [string trim $arg]
  294. if {$arg == [string tolower $trivia_q_answer]} {
  295. # try to stop any other output
  296. clearqueue server
  297. trivia_correct $nick
  298. return 0
  299. }
  300. #if they didn't get it right, and it's been more than 20 lines, taunt!
  301. if {($trivia_guesser_count > 0) && (($trivia_guesser_count % 20)) == 0} {
  302. putserv "PRIVMSG $trivia_channel :Playing with yourself, $nick? ;)"
  303. putlog "Is $nick playing with themselves?"
  304. }
  305. }
  306. #>>>
  307. # Someone got it right!
  308. proc trivia_correct { nick } {
  309. #<<<
  310. global trivia_q_id trivia_q_cat trivia_q_question trivia_q_answer trivia_q_hint trivia_q_attempts trivia_channel trivia_status
  311. global trivia_timer trivia_delay trivia_db_handle trivia_unanswered trivia_run_qty trivia_run_last trivia_run_nick trivia_c
  312. global trivia_score_time trivia_time_left_warning trivia_asking_question trivia_current_leader trivia_q_timestamp
  313. if {$trivia_status != 1} {
  314. #something strange going on
  315. return 0
  316. }
  317. trivia_killtimer
  318. set answer $trivia_q_answer
  319. set trivia_q_answer ""
  320. set newuser 0
  321. set trivia_asking_question 0
  322. set speed [expr [clock seconds] - $trivia_q_timestamp]
  323. set speedword ""
  324. if {$speed <= 1} {
  325. set speedword [trivia_random_element [list "POW! 0 to correct in under a second!" "Faster than a speeding fast thing!" "Even I didn't get it that quick, and I've just got to do a database lookup!"]]
  326. } elseif {$speed < 7} {
  327. set speedword [trivia_random_element [list "WHOOSH! 0 to correct in $speed seconds!" "%% shows their unassailable knowledge of whatever that question was about by getting it in $speed seconds" "Set course for correctsville, Commander %%. Ahead warp [expr 10 - $speed]!" "Caffeine abuse pays off for %% with a speedy time of $speed seconds" "Faster than a speeding triviabullet!"]]
  328. }
  329. set uid [trivia_get_uid $nick]
  330. if {$uid == 0} {
  331. putlog "$nick does not have entry in database... creating"
  332. set uid [trivia_create_user $nick]
  333. set newuser 1
  334. }
  335. putlog "$nick has uid $uid"
  336. set old_leader [trivia_leader]
  337. trivia_incr_score $uid
  338. regsub -all -nocase "%%" $speedword "$trivia_c(purple)$nick$trivia_c(off)" speedword
  339. putquick "PRIVMSG $trivia_channel :Congratulations $trivia_c(purple)$nick$trivia_c(off)! The answer was$trivia_c(purple) $answer$trivia_c(off)."
  340. if {$speedword != ""} {
  341. putquick "PRIVMSG $trivia_channel :$speedword"
  342. }
  343. trivia_bmotion_send "winner" "$nick $answer"
  344. if {$newuser == 1} {
  345. putquick "PRIVMSG $trivia_channel :Welcome to our newest player, $trivia_c(purple)$nick$trivia_c(off) :)"
  346. }
  347. putquick "PRIVMSG $trivia_channel :Rankings: [trivia_near_five3 $uid]"
  348. set leader [trivia_leader]
  349. if {($leader != $old_leader) && ($leader != -1) && ($old_leader != -1)} {
  350. putquick "PRIVMSG $trivia_channel :$trivia_c(purple) $nick $trivia_c(off) has taken the lead!"
  351. }
  352. #set score [trivia_get_score $uid]
  353. #putserv "PRIVMSG $trivia_channel :$nick now has $score points."
  354. set trivia_timer [utimer $trivia_delay trivia_start_round]
  355. set trivia_unanswered 0
  356. if {$trivia_run_last == $uid} {
  357. incr trivia_run_qty
  358. if {$trivia_run_qty > 2} {
  359. putquick "PRIVMSG $trivia_channel :$nick [trivia_get_run $trivia_run_qty] $trivia_run_qty in a row!"
  360. }
  361. } else {
  362. #end of streak
  363. if {$trivia_run_qty > 2} {
  364. putquick "PRIVMSG $trivia_channel :$nick ended $trivia_run_nick's winning spree."
  365. }
  366. set trivia_run_qty 1
  367. set trivia_run_last $uid
  368. set trivia_run_nick $nick
  369. }
  370. if {[rand 100] > 95} {
  371. putserv "PRIVMSG $trivia_channel :Remember, to report a problem with a question or answer, type $trivia_c(purple)!trivia report <description of problem>"
  372. }
  373. if {$trivia_score_time <= [clock seconds]} {
  374. trivia_end_week
  375. } else {
  376. if {[expr $trivia_score_time - [clock seconds]] < $trivia_time_left_warning} {
  377. set diff [expr $trivia_score_time - [clock seconds]]
  378. set diff $diff.0
  379. set nearness [expr $diff / $trivia_time_left_warning * 100]
  380. set chance [rand 100]
  381. putlog "diff = $diff, nearness = $nearness, chance = $chance"
  382. if {$chance < $nearness} {
  383. putserv "PRIVMSG $trivia_channel :[trivia_score_time_left] until the end of this game!"
  384. }
  385. }
  386. }
  387. trivia_check_rehash
  388. }
  389. #>>>
  390. #See if we need to rehash
  391. proc trivia_check_rehash { } {
  392. #<<<
  393. global trivia_must_rehash trivia_channel trivia_c
  394. if {$trivia_must_rehash == 1} {
  395. putquick "PRIVMSG $trivia_channel :$trivia_c(red)### Please wait, reloading trivia script ###"
  396. trivia_killtimer
  397. rehash
  398. }
  399. }
  400. #>>>
  401. # Turn a winning spree into text
  402. proc trivia_get_run { row } {
  403. #<<<
  404. global trivia_channel trivia_c
  405. switch $row {
  406. 3 {
  407. return "is on a winning spree!"
  408. }
  409. 4 {
  410. putquick "PRIVMSG $trivia_channel :$trivia_c(realblue)QUAD DAMAGE!"
  411. return "is on a roll ..."
  412. }
  413. 5 {
  414. return "is on a rampage!"
  415. }
  416. 6 {
  417. return "is unstoppable!"
  418. }
  419. 8 {
  420. return "is on a Google spree!"
  421. }
  422. 10 {
  423. return "is GODLIKE!"
  424. }
  425. 12 {
  426. return "needs to get out more."
  427. }
  428. }
  429. return "is on a roll ..."
  430. }
  431. #>>>
  432. # Escape stuff for SQL
  433. proc trivia_sqlite_escape { text } {
  434. #<<<
  435. return [string map {' ''} $text]
  436. }
  437. #>>>
  438. # Get someone's user ID from their nick
  439. proc trivia_get_uid { nick } {
  440. #<<<
  441. global trivia_db_handle
  442. putloglev 4 * "trivia_get_uid ($nick)"
  443. set nick [trivia_sqlite_escape $nick]
  444. set sql "SELECT user_id FROM users WHERE user_name = '$nick'"
  445. trivia_db_handle eval $sql {
  446. return $user_id
  447. }
  448. return 0
  449. }
  450. #>>>
  451. # Get a period
  452. proc trivia_get_period { } {
  453. #<<<
  454. return [expr [clock scan "last friday 23:59" -gmt true] + 60]
  455. }
  456. #>>>
  457. # Get a score from a UID
  458. proc trivia_get_score { user_id } {
  459. #<<<
  460. global trivia_db_handle
  461. putloglev 4 * "trivia_get_score ($user_id)"
  462. set dt [trivia_get_period]
  463. set sql "SELECT COUNT(*) AS yarr FROM scores WHERE dt > '$dt' AND user_id='$user_id'"
  464. trivia_db_handle eval $sql {
  465. return $yarr
  466. }
  467. return 0
  468. }
  469. #>>>
  470. # Turn a timestamp into a date
  471. proc trivia_ts_to_date { ts } {
  472. #<<<
  473. return [clock format $ts -format "%Y/%m/%d %H:%M"]
  474. }
  475. #>>>
  476. # Get someone's userinfo from their UID
  477. proc trivia_get_userinfo { user_id } {
  478. #<<<
  479. global trivia_db_handle trivia_channel
  480. putloglev 4 * "trivia_get_userinfo ($user_id)"
  481. set donestats 0
  482. set sql "SELECT user_name, user_score, user_last, user_reg FROM users WHERE user_id = '$user_id'"
  483. trivia_db_handle eval $sql {
  484. if {$user_last == ""} {
  485. set last "Unknown"
  486. } else {
  487. set last [trivia_ts_to_date $user_last]
  488. }
  489. if {$user_reg == ""} {
  490. set reg "Unkown"
  491. } else {
  492. set reg [trivia_ts_to_date $user_reg]
  493. }
  494. putserv "PRIVMSG $trivia_channel :Trivia stats for $user_name:"
  495. putserv "PRIVMSG $trivia_channel : Current score: $user_score"
  496. putserv "PRIVMSG $trivia_channel : First seen: $reg"
  497. putserv "PRIVMSG $trivia_channel : Last scored: $last"
  498. set donestats 1
  499. }
  500. set sql "SELECT COUNT(dt) AS score FROM scores WHERE dt > [trivia_get_period] AND user_id = '$user_id'"
  501. if {$donestats} {
  502. trivia_db_handle eval $sql {
  503. putserv "PRIVMSG $trivia_channel :Points this week: $score"
  504. putserv "PRIVMSG $trivia_channel : Weekly Ranking: [trivia_get_rank $user_id]"
  505. }
  506. } else {
  507. putserv "PRIVMSG $trivia_channel :No stats found"
  508. }
  509. }
  510. #>>>
  511. # Get someone's stats from their username
  512. proc trivia_user_stats { user_name } {
  513. #<<<
  514. global trivia_channel
  515. set uid [trivia_get_uid $user_name]
  516. if {$uid == 0} {
  517. putserv "PRIVMSG $trivia_channel :Unknown user '$user_name' (is the case right?)"
  518. return
  519. }
  520. trivia_get_userinfo $uid
  521. }
  522. #>>>
  523. # Increase a UID's score
  524. proc trivia_incr_score { id { howmuch 1 } } {
  525. #<<<
  526. global trivia_db_handle
  527. putloglev 4 * "trivia_incr_score ($id, $howmuch)"
  528. set sql "UPDATE users SET user_last = [clock seconds], user_points = user_points + 1 WHERE user_id = '$id'"
  529. trivia_db_handle eval $sql
  530. set sql "INSERT INTO scores VALUES ('$id', '[clock seconds]')"
  531. trivia_db_handle eval $sql
  532. }
  533. #>>>
  534. # Create a new user
  535. proc trivia_create_user { nick } {
  536. #<<<
  537. global trivia_db_handle
  538. putloglev 4 * "trivia_create_user ($nick)"
  539. set nick [trivia_sqlite_escape $nick]
  540. set sql "INSERT INTO users VALUES (null, '$nick', '', 0, [clock seconds], [clock seconds], 0)"
  541. trivia_db_handle eval $sql
  542. set uid [trivia_db_handle last_insert_rowid]
  543. return $uid
  544. }
  545. #>>>
  546. # Handle a !trivia command
  547. proc trivia_command { nick host handle channel param } {
  548. #<<<
  549. global trivia_channel trivia_status trivia_flag trivia_admin trivia_c
  550. set arg ""
  551. regexp {^([^ ]+)( .+)?$} $param matches cmd arg
  552. set param [string tolower $cmd]
  553. set arg [string trim $arg]
  554. putloglev d * "trivia command: $param from: $nick ($arg)"
  555. if {[matchattr $handle |$trivia_admin $trivia_channel] && ($param != "enable") && ($trivia_status == -1)} {
  556. return 0
  557. }
  558. if [regexp -nocase "^(score|place)( .+)?" $param] {
  559. putserv "PRIVMSG $trivia_channel :[trivia_score $arg $nick]"
  560. if [string match -nocase $nick $arg] {
  561. putserv "PRIVMSG $trivia_channel :(You know putting your own nick is optional, right? :)"
  562. }
  563. return 0
  564. }
  565. if {$param == "top10"} {
  566. putserv "PRIVMSG $trivia_channel :[trivia_get_top10]"
  567. return 0
  568. }
  569. if {$param == "info"} {
  570. trivia_user_stats $arg
  571. return 0
  572. }
  573. if {$param == "start"} {
  574. trivia_start
  575. return 0
  576. }
  577. if {$param == "report"} {
  578. trivia_report $nick $arg
  579. return 0
  580. }
  581. if {$param == "countdown"} {
  582. trivia_countdown
  583. return 0
  584. }
  585. if {![matchattr $handle |$trivia_flag $channel]} {
  586. putserv "PRIVMSG $nick :use: !trivia \[score|top10|start|report\]"
  587. return 0
  588. }
  589. if {$param == "stop"} {
  590. trivia_stop
  591. return 0
  592. }
  593. if {$param == "skip"} {
  594. trivia_skip $nick
  595. return 0
  596. }
  597. if {$param == "stats"} {
  598. trivia_stats
  599. return 0
  600. }
  601. if {![matchattr $handle |$trivia_admin $channel]} {
  602. putserv "PRIVMSG $nick :use: !trivia \[score|top10|start|stop|skip|stats\]"
  603. return 0
  604. }
  605. if {$param == "disable"} {
  606. trivia_disable
  607. return 0
  608. }
  609. if {$param == "enable"} {
  610. trivia_enable
  611. return 0
  612. }
  613. if {$param == "merge"} {
  614. trivia_merge $nick $arg
  615. #puthelp "PRIVMSG $trivia_channel :Score merging is disabled"
  616. return 0
  617. }
  618. if {$param == "rehash"} {
  619. trivia_rehash
  620. return 0
  621. }
  622. putserv "PRIVMSG $nick :use: !trivia \[start|stop|score|top10|skip|stats|enable|disable|merge\]"
  623. return 0
  624. }
  625. #>>>
  626. # Rehash
  627. proc trivia_rehash { } {
  628. #<<<
  629. global trivia_status trivia_channel trivia_must_rehash
  630. if {$trivia_must_rehash == 1} {
  631. putserv "PRIVMSG $trivia_channel :! Reloading trivia now..."
  632. rehash
  633. return 0
  634. }
  635. if {$trivia_status != 1} {
  636. putserv "PRIVMSG $trivia_channel :! Reloading trivia now..."
  637. rehash
  638. return 0
  639. }
  640. set trivia_must_rehash 1
  641. putserv "PRIVMSG $trivia_channel :Trivia will reload after current round"
  642. }
  643. #>>>
  644. # Disable the script
  645. proc trivia_disable { } {
  646. #<<<
  647. global trivia_status trivia_channel
  648. if {$trivia_status == 1} {
  649. #stop the current game
  650. trivia_stop
  651. }
  652. set trivia_status -1
  653. putserv "PRIVMSG $trivia_channel :Trivia disabled."
  654. return 0
  655. }
  656. #>>>
  657. proc trivia_ping { } {
  658. return
  659. }
  660. # Enable the script
  661. proc trivia_enable {} {
  662. #<<<
  663. global trivia_status trivia_channel trivia_db_handle
  664. set trivia_status 0
  665. putserv "PRIVMSG $trivia_channel :Trivia enabled."
  666. trivia_ping
  667. return 0
  668. }
  669. #>>>
  670. #Make a hint
  671. proc trivia_make_hint { hint answer } {
  672. #<<<
  673. global trivia_debug_mode
  674. set hint [string toupper $hint]
  675. set answer [string toupper $answer]
  676. set answer_words [split $answer { }]
  677. set final_hint ""
  678. #are we making the very first hint?
  679. if {$hint == ""} {
  680. foreach word $answer_words {
  681. append final_hint [string repeat "_" [string length $word]]
  682. append final_hint " "
  683. }
  684. set final_hint [string trim $final_hint]
  685. #now put the punctuation in
  686. set letters [split $answer {}]
  687. set i 0
  688. foreach letter $letters {
  689. if {![regexp -nocase {[A-Z0-9 ]} $letter]} {
  690. set final_hint [string replace $final_hint $i $i $letter]
  691. }
  692. incr i
  693. }
  694. return [string trim $final_hint]
  695. }
  696. # explode the into words
  697. set hint_words [split $hint { }]
  698. set i 0
  699. foreach word $hint_words {
  700. set answer_word [lindex $answer_words $i]
  701. putloglev 1 * "considering $answer_word ($word)"
  702. #are we on the first iteration?
  703. if [regexp "^_+$" $word] {
  704. #use the first letter
  705. set letters [string length $word]
  706. #don't hint for single-letter words
  707. if {$letters > 1} {
  708. set word [string index $answer_word 0]
  709. append word [string repeat "_" [expr $letters - 1]]
  710. } else {
  711. set word "_"
  712. }
  713. append final_hint "$word "
  714. } else {
  715. #not the first iteration so figure out where the gaps are
  716. set gaps [list]
  717. set letters [split $word {}]
  718. set j 0
  719. foreach letter $letters {
  720. if {$letter == "_"} {
  721. lappend gaps $j
  722. }
  723. incr j
  724. }
  725. if {[llength $gaps] <= 1} {
  726. #eek no letters to replace, or only one gap
  727. putloglev 1 * " insufficient gaps, using $word"
  728. append final_hint "$word "
  729. } else {
  730. #now we pick a random letter position
  731. set pos [trivia_random_element $gaps]
  732. putloglev 1 * " filling in $pos"
  733. set word [string replace $word $pos $pos [string index $answer_word $pos]]
  734. putloglev 1 * " --> $word"
  735. append final_hint "$word "
  736. }
  737. }
  738. incr i
  739. }
  740. set final_hint [string trim $final_hint]
  741. if {$trivia_debug_mode == 1} {
  742. return $answer
  743. }
  744. return $final_hint
  745. }
  746. #>>>
  747. # Fetch a question
  748. proc trivia_get_question { } {
  749. #<<<
  750. global trivia_db_handle trivia_q_id trivia_q_cat trivia_q_question trivia_q_answer trivia_q_hint trivia_channel
  751. set trivia_q_id ""
  752. set sql "SELECT q.question, q.question_id, q.answer, c.cat_name FROM questions q LEFT JOIN categories c USING (cat_id) WHERE c.cat_enabled=1 ORDER BY count ASC, random() LIMIT 1"
  753. trivia_db_handle eval $sql {
  754. set trivia_q_id $question_id
  755. set trivia_q_cat $cat_name
  756. set trivia_q_question $question
  757. set trivia_q_answer [string toupper $answer]
  758. set trivia_q_hint ""
  759. #tidy up question and answer
  760. regsub -all " +" $trivia_q_question " " trivia_q_question
  761. regsub -all " +" $trivia_q_answer " " trivia_q_answer
  762. set trivia_q_question [string trim $trivia_q_question]
  763. set trivia_q_answer [string trim $trivia_q_answer]
  764. }
  765. if {$trivia_q_id == ""} {
  766. return
  767. }
  768. #update the times used
  769. set sql "UPDATE questions SET count = count + 1 WHERE question_id = '$trivia_q_id'"
  770. trivia_db_handle eval $sql
  771. }
  772. #>>>
  773. # Start a round
  774. proc trivia_start_round { } {
  775. #<<<
  776. global trivia_q_id trivia_q_cat trivia_q_question trivia_q_answer trivia_q_hint trivia_q_attempts trivia_channel trivia_status trivia_last_qid
  777. global trivia_asking_question trivia_delay botnick trivia_q_timestamp
  778. if {$trivia_status != 1} {
  779. #we're switched off, abort
  780. return 0
  781. }
  782. #init variables
  783. set trivia_q_id ""
  784. set trivia_q_cat ""
  785. set trivia_q_question ""
  786. set trivia_q_answer ""
  787. set trivia_q_hint ""
  788. putlog "Fetching next question..."
  789. trivia_get_question
  790. if {$trivia_q_id == ""} {
  791. putlog "Couldn't init trivia round, aborting."
  792. return
  793. }
  794. putlog "Successfully fetched question $trivia_q_id from database"
  795. putloglev 4 * "question = $trivia_q_question"
  796. putloglev 4 * "answer = $trivia_q_answer"
  797. set trivia_q_attempts 1
  798. set trivia_last_qid $trivia_q_id
  799. set trivia_asking_question 1
  800. trivia_bmotion_send "start" "$trivia_channel $trivia_delay $botnick"
  801. set trivia_q_timestamp [clock seconds]
  802. trivia_round
  803. }
  804. #>>>
  805. # Run a round
  806. proc trivia_round { } {
  807. #<<<
  808. global trivia_q_id trivia_q_cat trivia_q_question trivia_q_answer trivia_q_hint trivia_q_attempts trivia_channel trivia_status
  809. global trivia_timer trivia_speed trivia_c trivia_last_ts
  810. if {$trivia_status != 1} {
  811. #we're switched off, abort
  812. return 0
  813. }
  814. if {$trivia_q_attempts == 5} {
  815. trivia_end_round
  816. return 0
  817. }
  818. if {$trivia_q_answer == ""} {
  819. return 0
  820. }
  821. #update the hint
  822. set trivia_q_hint [trivia_make_hint $trivia_q_hint $trivia_q_answer]
  823. putlog "hint is $trivia_q_hint"
  824. #say our stuff
  825. if {$trivia_q_attempts > 1} {
  826. set hint " \[[expr $trivia_q_attempts - 1] of 3\]"
  827. } else {
  828. set hint ""
  829. }
  830. set split_question [trivia_question_split $trivia_q_question]
  831. putquick "PRIVMSG $trivia_channel :$trivia_c(red)--== Trivia ==--$trivia_c(off) \[category: \002$trivia_q_cat\002\]"
  832. foreach q $split_question {
  833. if {$q != ""} {
  834. putquick "PRIVMSG $trivia_channel :$trivia_c(blue) [trivia_question_inject $q]"
  835. }
  836. }
  837. #set new_question [trivia_question_inject $trivia_q_question]
  838. #putquick "PRIVMSG $trivia_channel :$trivia_c(blue) $new_question"
  839. putquick "PRIVMSG $trivia_channel :Hint$hint: [trivia_explode $trivia_q_hint]"
  840. incr trivia_q_attempts
  841. trivia_bmotion_send "hint" [string map {_ .} $trivia_q_hint]
  842. set trivia_last_ts [clock seconds]
  843. global trivia_must_rehash
  844. if {$trivia_must_rehash != 2} {
  845. set trivia_timer [utimer $trivia_speed trivia_round]
  846. }
  847. }
  848. #>>>
  849. # Make it harder for things to break questions (inject bold codes)
  850. proc trivia_question_inject { question } {
  851. #<<<
  852. putlog "trivia_question_inject $question"
  853. #inject random bold/underline/colour codes into question
  854. global trivia_c
  855. set l [string length $question]
  856. putlog "length: $l"
  857. if {$l < 1} {
  858. return $question
  859. }
  860. set pos [rand $l]
  861. set first [string range $question 0 $pos]
  862. incr pos
  863. set second [string range $question $pos end]
  864. switch [rand 1] {
  865. 0 {
  866. putlog "question_inject: using bold at pos $pos"
  867. return $first$trivia_c(bold)$trivia_c(bold)$second
  868. }
  869. 1 {
  870. putlog "question_inject: using purple at pos $pos"
  871. return $first$trivia_c(purple)$trivia_c(off)$second
  872. }
  873. }
  874. return $question
  875. }
  876. #>>>
  877. # Make it harder for things to break questions (inject line breaks)
  878. proc trivia_question_split { question } {
  879. #<<<
  880. putlog "trivia_question_split $question"
  881. #explodes a question into two lines at word boundaries, if it's long enough
  882. #don't split unscrambles incorrectly
  883. if [regexp -nocase "(unscramble .+:) (\[A-Z \]+)" $question matches first second] {
  884. return [list $first $second]
  885. }
  886. set words [split $question " "]
  887. set wordcount [llength $words]
  888. if {$wordcount < 4} {
  889. putlog "aborting, too short"
  890. return [list $question]
  891. }
  892. #enough words to split
  893. #we want at least two on the first line
  894. putlog "wordcount: $wordcount"
  895. incr wordcount -2
  896. if {$wordcount < 0} {
  897. return [list $question]
  898. }
  899. set pos [rand $wordcount]
  900. incr pos 2
  901. putlog "picked pos $pos"
  902. set wordlist [list]
  903. set i 0
  904. set line ""
  905. foreach word $words {
  906. #putlog "word = $word, i = $i"
  907. append line "$word "
  908. if {$i == $pos} {
  909. #putlog "splitting here, line = $line"
  910. lappend wordlist [string trim $line]
  911. set line ""
  912. }
  913. incr i
  914. }
  915. #putlog "done, appending $line to list"
  916. if {$line != ""} {
  917. lappend wordlist [string trim $line]
  918. }
  919. #putlog "split question list is: $wordlist"
  920. return $wordlist
  921. }
  922. #>>>
  923. # Finish a round (without a winner)
  924. proc trivia_end_round { } {
  925. #<<<
  926. global trivia_q_id trivia_q_cat trivia_q_question trivia_q_answer trivia_q_hint trivia_q_attempts trivia_channel trivia_status
  927. global trivia_timer trivia_delay trivia_db_handle trivia_unanswered
  928. global trivia_run_last trivia_run_nick trivia_run_qty trivia_c trivia_score_time trivia_asking_question trivia_time_left_warning
  929. if {$trivia_status != 1} {
  930. #we're switched off, abort
  931. return 0
  932. }
  933. set trivia_asking_question 0
  934. trivia_bmotion_send "winner" "* $trivia_q_answer"
  935. trivia_bmotion_send "stop" ""
  936. set trivia_q_answer [string toupper $trivia_q_answer]
  937. putquick "PRIVMSG $trivia_channel :Time's up! Nobody got it right. The answer was$trivia_c(purple) $trivia_q_answer"
  938. set trivia_q_answer ""
  939. incr trivia_unanswered
  940. if {$trivia_run_qty > 2} {
  941. putserv "PRIVMSG $trivia_channel :So much for $trivia_run_nick's winning spree."
  942. }
  943. set trivia_run_last 0
  944. set trivia_run_qty 0
  945. set trivia_run_nick ""
  946. if {[rand 100] > 90} {
  947. putserv "PRIVMSG $trivia_channel :Remember, to report a problem with a question or answer, type $trivia_c(purple)!trivia report <description of problem>"
  948. }
  949. if {$trivia_unanswered > 3} {
  950. putserv "PRIVMSG $trivia_channel :Four unanswered in a row, stopping the game."
  951. putserv "PRIVMSG $trivia_channel :You can restart it with $trivia_c(purple)!t start"
  952. set trivia_status 0
  953. } else {
  954. set trivia_timer [utimer $trivia_delay trivia_start_round]
  955. if {$trivia_score_time <= [clock seconds]} {
  956. trivia_end_week
  957. } else {
  958. if {[expr $trivia_score_time - [clock seconds]] < $trivia_time_left_warning} {
  959. set diff [expr $trivia_score_time - [clock seconds]]
  960. set diff $diff.0
  961. set nearness [expr $diff / $trivia_time_left_warning * 100]
  962. set chance [rand 100]
  963. putlog "diff = $diff, nearness = $nearness, chance = $chance"
  964. if {$chance < $nearness} {
  965. putserv "PRIVMSG $trivia_channel :[trivia_score_time_left] until the end of this game!"
  966. }
  967. }
  968. }
  969. trivia_check_rehash
  970. }
  971. }
  972. #>>>
  973. # Skip the rest of this question
  974. proc trivia_skip { nick } {
  975. #<<<
  976. global trivia_q_id trivia_q_cat trivia_q_question trivia_q_answer trivia_q_hint trivia_q_attempts trivia_channel trivia_status
  977. global trivia_timer trivia_delay trivia_db_handle trivia_unanswered trivia_score_time
  978. if {$trivia_status != 1} {
  979. #we're switched off, abort
  980. return 0
  981. }
  982. if {$trivia_q_answer == ""} {
  983. #no round in progress
  984. return 0
  985. }
  986. set trivia_q_question ""
  987. set trivia_q_answer ""
  988. trivia_killtimer
  989. trivia_bmotion_send "stop" ""
  990. putserv "PRIVMSG $trivia_channel :Skipping this question by $nick's request."
  991. set trivia_timer [utimer $trivia_delay trivia_start_round]
  992. if {$trivia_score_time <= [clock seconds]} {
  993. trivia_end_week
  994. }
  995. }
  996. #>>>
  997. # Utility function to fetch a random item from a list
  998. proc trivia_random_element { l } {
  999. #<<<
  1000. return [lindex $l [rand [llength $l]]]
  1001. }
  1002. #>>>
  1003. # Start the game
  1004. proc trivia_start { } {
  1005. #<<<
  1006. global trivia_status trivia_channel trivia_unanswered trivia_run_last trivia_run_qty trivia_c trivia_must_rehash trivia_db_handle
  1007. if {$trivia_status == 1} {
  1008. putserv "PRIVMSG $trivia_channel :Trivia is already running."
  1009. return 0
  1010. }
  1011. if {$trivia_status == -1} {
  1012. putserv "PRIVMSG $trivia_channel :Trivia is currently disabled."
  1013. return 0
  1014. }
  1015. trivia_ping
  1016. # go go go
  1017. set trivia_status 1
  1018. if {$trivia_must_rehash == 2 } {
  1019. putquick "PRIVMSG $trivia_channel :$trivia_c(blue)### Resuming trivia game ###" -next
  1020. } else {
  1021. putquick "PRIVMSG $trivia_channel :Trivia game started!" -next
  1022. }
  1023. #trivia_stats
  1024. putquick "PRIVMSG $trivia_channel :[trivia_score_time_left] left until end of this game!"
  1025. #try to find last week's winner
  1026. set sql "SELECT winners.user_id, users.user_name FROM winners LEFT JOIN users USING(user_id) ORDER BY dt DESC, score DESC LIMIT 1"
  1027. putloglev d * $sql
  1028. trivia_db_handle eval $sql {
  1029. putquick "PRIVMSG $trivia_channel :Last week's winner was$trivia_c(purple) [string toupper $user_name]$trivia_c(off)!"
  1030. }
  1031. set trivia_unanswered 0
  1032. set trivia_run_last 0
  1033. set trivia_run_qty 0
  1034. trivia_start_round
  1035. return 0
  1036. }
  1037. #>>>
  1038. # Stop the game
  1039. proc trivia_stop { } {
  1040. #<<<
  1041. global trivia_channel trivia_status
  1042. if {$trivia_status == 1} {
  1043. clearqueue server
  1044. putserv "PRIVMSG $trivia_channel :Trivia game stopped."
  1045. set trivia_status 0
  1046. trivia_bmotion_send "stop" ""
  1047. trivia_killtimer
  1048. }
  1049. return 0
  1050. }
  1051. #>>>
  1052. # Kill our timer
  1053. proc trivia_killtimer { } {
  1054. #<<<
  1055. global trivia_timer
  1056. if {$trivia_timer != ""} {
  1057. killutimer $trivia_timer
  1058. }
  1059. }
  1060. #>>>
  1061. # Utility function to explode line into letters
  1062. proc trivia_explode { line } {
  1063. #<<<
  1064. set letters [split $line {}]
  1065. set newline ""
  1066. foreach letter $letters {
  1067. append newline "$letter "
  1068. }
  1069. return [string trim $newline]
  1070. }
  1071. #>>>
  1072. # Get a UID's ranking
  1073. proc trivia_get_rank { uid } {
  1074. #<<<
  1075. global trivia_db_handle
  1076. putloglev 4 * "trivia_get_rank ($uid)"
  1077. set sql "SELECT user_id FROM users ORDER by user_score DESC"
  1078. set dt [trivia_get_period]
  1079. set sql "select user_id, count(dt) as d from scores where dt > $dt group by user_id order by d desc"
  1080. set pos 0
  1081. trivia_db_handle eval $sql {
  1082. incr pos
  1083. putloglev d * "considering user_id $user_id, score $d"
  1084. if {$user_id == $uid} {
  1085. return $pos
  1086. }
  1087. }
  1088. return 0
  1089. }
  1090. #>>>
  1091. # Get the top 10 users
  1092. proc trivia_get_top10 { } {
  1093. #<<<
  1094. global trivia_db_handle
  1095. set dt [trivia_get_period]
  1096. set sql "select users.user_name as u, count(dt) as d from scores left join users using (user_id) where dt > $dt group by scores.user_id order by d desc limit 10"
  1097. set output ""
  1098. set index 0
  1099. trivia_db_handle eval $sql {
  1100. incr index
  1101. append output "\002$index:\002 "
  1102. append output $u
  1103. append output " ("
  1104. append output $d
  1105. append output ") "
  1106. }
  1107. if {$index == 0} {
  1108. set output "No scores so far, everyone is equal first! \\o/"
  1109. } else {
  1110. set output "The top $index players are... $output"
  1111. }
  1112. return $output
  1113. }
  1114. #>>>
  1115. # Fix a number into text
  1116. proc trivia_ordinal { number } {
  1117. #<<<
  1118. if [regexp {1[0-9]$} $number] {
  1119. return "th"
  1120. }
  1121. set last [string range $number end end]
  1122. if {$last == "1"} {
  1123. return "st"
  1124. }
  1125. if {$last == "2"} {
  1126. return "nd"
  1127. }
  1128. if {$last == "3"} {
  1129. return "rd"
  1130. }
  1131. return "th"
  1132. }
  1133. #>>>
  1134. # Get a score
  1135. proc trivia_score { n { nick "" } } {
  1136. #<<<
  1137. set n [string trim $n]
  1138. putloglev 4 * "trivia_score($n, $nick)"
  1139. if {$n == ""} {
  1140. set ni $nick
  1141. set o "You are"
  1142. } else {
  1143. set ni $n
  1144. set o "$n is"
  1145. }
  1146. set uid [trivia_get_uid $ni]
  1147. set score [trivia_get_score $uid]
  1148. set pos [trivia_get_rank $uid]
  1149. if {$score == 0} {
  1150. return "No score found for $n."
  1151. }
  1152. return "$o ranked $pos[trivia_ordinal $pos] with $score points."
  1153. }
  1154. #>>>
  1155. # Turn a list into a stats list
  1156. proc trivia_list_to_stats { pos user_id l } {
  1157. #<<<
  1158. putloglev 4 * "trivia_list_to_stats ($pos, $user_id, $l)"
  1159. global trivia_c
  1160. set uid [lindex $l 0]
  1161. set nick [lindex $l 1]
  1162. set score [lindex $l 2]
  1163. incr pos
  1164. set result ""
  1165. if {$user_id == $uid} {
  1166. set result "$trivia_c(purple)$trivia_c(bold)"
  1167. }
  1168. append result "$pos[trivia_ordinal $pos]: $nick ($score) "
  1169. if {$user_id == $uid} {
  1170. append result "$trivia_c(off)$trivia_c(bold)"
  1171. }
  1172. return $result
  1173. }
  1174. #>>>
  1175. # Another function to get the nearest users to your score
  1176. proc trivia_near_five2 { uid } {
  1177. #<<<
  1178. global trivia_db_handle trivia_c
  1179. set sql "SELECT user_id, user_name, user_score FROM users ORDER BY user_score DESC"
  1180. set dt [trivia_get_period]
  1181. set sql "select users.user_id as user_id, users.user_name as user_name, count(dt) as user_score from scores left join users using (user_id) where dt > $dt group by scores.user_id order by count(dt) desc"
  1182. putlog $sql
  1183. set result [list]
  1184. trivia_db_handle eval $sql {
  1185. set line [list]
  1186. lappend line $user_id
  1187. lappend line [trivia_question_inject $user_name]
  1188. lappend line $user_score
  1189. putlog "adding line $line"
  1190. lappend result $line
  1191. }
  1192. putlog "results list: $result"
  1193. set position 0
  1194. foreach item $result {
  1195. if {[lindex $item 0] == $uid} {
  1196. putlog "found at position $position"
  1197. break
  1198. }
  1199. incr position
  1200. }
  1201. set line ""
  1202. if {$position > 2} {
  1203. append line [trivia_list_to_stats [expr $position - 2] $uid [lindex $result [expr $position - 2]]]
  1204. }
  1205. if {$position > 1} {
  1206. append line [trivia_list_to_stats [expr $position - 1] $uid [lindex $result [expr $position - 1]]]
  1207. }
  1208. append line [trivia_list_to_stats $position $uid [lindex $result $position]]
  1209. if {$position < [expr [llength $result] - 1]} {
  1210. append line [trivia_list_to_stats [expr $position + 1] $uid [lindex $result [expr $position + 1]]]
  1211. }
  1212. if {$position < [expr [llength $result] - 2]} {
  1213. append line [trivia_list_to_stats [expr $position + 2] $uid [lindex $result [expr $position + 2]]]
  1214. }
  1215. return $line
  1216. }
  1217. #>>>
  1218. # Third function to get the nearest users to your score
  1219. proc trivia_near_five3 { uid } {
  1220. global trivia_db_handle trivia_c
  1221. set sql "DROP TABLE IF EXISTS _score"
  1222. putlog $sql
  1223. trivia_db_handle eval $sql
  1224. set sql "CREATE TEMPORARY TABLE _score (user_id int, user_score int, last_score int)"
  1225. putlog $sql
  1226. trivia_db_handle eval $sql
  1227. set sql "INSERT INTO _score SELECT user_id, COUNT(dt), MAX(dt) AS user_score FROM scores GROUP BY scores.user_id"
  1228. putlog $sql
  1229. trivia_db_handle eval $sql
  1230. set outputlist ""
  1231. # get our score
  1232. set sql "SELECT user_score, last_score FROM _score WHERE user_id = $uid"
  1233. set our_score ""
  1234. set our_last ""
  1235. trivia_db_handle eval $sql {
  1236. set our_score $user_score
  1237. set our_last $last_score
  1238. }
  1239. if {$our_score == ""} {
  1240. return ""
  1241. }
  1242. if {$last_score == ""} {
  1243. return ""
  1244. }
  1245. #find our position
  1246. #don't need to use last_score here because we must've just scored - making use the most recent point for this score
  1247. set sql "SELECT user_id FROM _score WHERE user_score >= $our_score ORDER BY user_score DESC, last_score DESC"
  1248. putlog $sql
  1249. set position 1
  1250. trivia_db_handle eval $sql {
  1251. if {$user_id != $uid} {
  1252. incr position
  1253. } else {
  1254. break
  1255. }
  1256. }
  1257. putlog "our position is $position"
  1258. if {$position == 1} {
  1259. putlog "oh we're first"
  1260. #set output "$trivia_c(purple) $trivia_c(bold)"
  1261. #append output "1st: [trivia_get_username $uid] ($our_score) $trivia_c(off)$trivia_c(bold)"
  1262. lappend outputlist [list $uid $our_score]
  1263. } else {
  1264. #find some users higher than us
  1265. set sql "SELECT * FROM _score WHERE user_score > $user_score ORDER BY user_score ASC, last_score ASC LIMIT 4"
  1266. putlog $sql
  1267. set prelist [list]
  1268. trivia_db_handle eval $sql {
  1269. set outputlist [concat [list [list $user_id $user_score] ] $outputlist]
  1270. putlog "outputlist is now $outputlist"
  1271. }
  1272. # finally, us
  1273. lappend outputlist [list $uid $our_score]
  1274. }
  1275. putlog "so far, output list is $outputlist"
  1276. # find two people below us
  1277. set sql "SELECT * FROM _score WHERE (user_score < $our_score) OR ((user_score = $our_score) AND (last_score < $our_last)) ORDER BY user_score DESC, last_score DESC LIMIT 5"
  1278. putlog $sql
  1279. set postlist [list]
  1280. trivia_db_handle eval $sql {
  1281. lappend postlist [list $user_id $user_score]
  1282. }
  1283. putlog "postlist is $postlist"
  1284. set pre_size 3
  1285. set post_size 2
  1286. if {$position == 1} {
  1287. putlog "we're in first"
  1288. set pre_size 1
  1289. set post_size 4
  1290. }
  1291. if {$position == 2} {
  1292. putlog "we're in 2nd"
  1293. set pre_size 2
  1294. set post_size 3
  1295. }
  1296. if {[llength $postlist] == 0} {
  1297. putlog "no postlist, using only prelist"
  1298. set pre_size 5
  1299. }
  1300. if {[llength $postlist] == 1} {
  1301. putlog "one postlist, using most prelist"
  1302. set pre_size 4
  1303. }
  1304. if {$pre_size < [llength $outputlist]} {
  1305. putlog "pre_size $pre_size < length of prelist, trimming"
  1306. set startpos [expr [llength $outputlist] - $pre_size]
  1307. set outputlist [lrange $outputlist $startpos end]
  1308. }
  1309. putlog "outputlist length is [llength $outputlist]"
  1310. set post_size [expr 5 - [llength $outputlist]]
  1311. if {$post_size < [llength $postlist]} {
  1312. putlog "post_size calculated as $post_size and is less than list length"
  1313. set postlist [lrange $postlist 0 [expr $post_size - 1]]
  1314. }
  1315. putlog "---"
  1316. putlog "pre: $outputlist"
  1317. putlog "post: $postlist"
  1318. if {$position > 2} {
  1319. set initpos [expr $position - 2]
  1320. } else {
  1321. set initpos 1
  1322. }
  1323. putlog "first position on list is $initpos"
  1324. foreach place $postlist {
  1325. lappend outputlist $place
  1326. }
  1327. set output ""
  1328. foreach place $outputlist {
  1329. set entry ""
  1330. append entry "$initpos"
  1331. append entry [trivia_ordinal $initpos]
  1332. append entry ": "
  1333. append entry [trivia_question_inject [trivia_get_username [lindex $place 0]]]
  1334. append entry " ("
  1335. append entry [lindex $place 1]
  1336. append entry ")"
  1337. incr initpos
  1338. if {[lindex $place 0] == $uid} {
  1339. append output "$trivia_c(purple)$trivia_c(bold) $entry$trivia_c(off)$trivia_c(bold) "
  1340. } else {
  1341. append output " $entry "
  1342. }
  1343. }
  1344. putlog $output
  1345. return $output
  1346. }
  1347. proc trivia_get_username { uid } {
  1348. global trivia_db_handle
  1349. set sql "SELECT user_name FROM users WHERE user_id = $uid"
  1350. putlog $sql
  1351. return [trivia_db_handle onecolumn $sql]
  1352. }
  1353. # Get the current leader's UID
  1354. proc trivia_leader { } {
  1355. global trivia_db_handle
  1356. set dt [trivia_get_period]
  1357. set sql "SELECT user_id, count(dt) AS user_score FROM scores WHERE dt > $dt GROUP BY user_id ORDER BY COUNT(dt) DESC LIMIT 1"
  1358. set uid -1
  1359. trivia_db_handle eval $sql {
  1360. set uid $user_id
  1361. }
  1362. return $uid
  1363. }
  1364. # Get some stats from the DB
  1365. proc trivia_stats { } {
  1366. #<<<
  1367. global trivia_db_handle trivia_channel
  1368. set sql "SELECT COUNT(*) AS total FROM questions LEFT JOIN categories USING (cat_id) WHERE categories.cat_enabled = 1;"
  1369. set stat_total_questions [trivia_db_handle eval $sql]
  1370. set sql "SELECT COUNT(*) AS total FROM categories WHERE cat_enabled = 1"
  1371. set stat_total_cats [trivia_db_handle eval $sql]
  1372. putserv "PRIVMSG $trivia_channel :Using\002 $stat_total_questions\002 questions in\002 $stat_total_cats\002 categories."
  1373. }
  1374. #>>>
  1375. # Merge two users
  1376. proc trivia_merge { nick param } {
  1377. #<<<
  1378. global trivia_db_handle
  1379. putloglev 4 * "trivia_merge ($nick, $param)"
  1380. if [regexp {^([^ ]+) (.+)$} $param matches nick1 nick2] {
  1381. set olduid [trivia_get_uid [trivia_sqlite_escape $nick1]]
  1382. set newuid [trivia_get_uid [trivia_sqlite_escape $nick2]]
  1383. if {$olduid == 0} {
  1384. puthelp "PRIVMSG $nick :Can't find user '$nick1'"
  1385. return 0
  1386. }
  1387. if {$newuid == 0} {
  1388. puthelp "PRIVMSG $nick :Can't find user '$nick2'"
  1389. return 0
  1390. }
  1391. puthelp "PRIVMSG $nick :Meging score for $nick1 into score for $nick2"
  1392. set sql "UPDATE scores SET user_id=$newuid WHERE user_id=$olduid"
  1393. trivia_db_handle eval $sql
  1394. set newscore [trivia_get_score $newuid]
  1395. puthelp "PRIVMSG $nick :$nick2 now has $newscore points."
  1396. set sql "DELETE FROM users WHERE user_id = $olduid"
  1397. trivia_db_handle eval $sql
  1398. puthelp "PRIVMSG $nick :User $nick1 has been deleted."
  1399. return 0
  1400. } else {
  1401. puthelp "PRIVMSG $nick :Use: !trivia merge <olduser> <newuser>"
  1402. puthelp "PRIVMSG $nick : olduser's score is merged with newuser's and olduser is deleted."
  1403. }
  1404. }
  1405. #>>>
  1406. # Report a broken question
  1407. proc trivia_report { nick msg } {
  1408. #<<<
  1409. global trivia_channel trivia_db_handle trivia_last_qid trivia_c
  1410. if {$trivia_last_qid == 0} {
  1411. puthelp "PRIVMSG $trivia_channel :Sorry, unable to work out which question to report on :("
  1412. return 0
  1413. }
  1414. set nick [trivia_sqlite_escape $nick]
  1415. set msg [trivia_sqlite_escape $msg]
  1416. set sql "INSERT INTO reports VALUES (null, [clock seconds], '$nick', '$trivia_last_qid', '$msg', 'N')"
  1417. trivia_db_handle eval $sql
  1418. puthelp "PRIVMSG $trivia_channel :Added a report against question ID$trivia_c(purple) $trivia_last_qid$trivia_c(off) from $trivia_c(purple)$nick$trivia_c(off)."
  1419. set trivia_last_qid 0
  1420. }
  1421. #>>>
  1422. ### WATCHDOG STUFF <<<
  1423. # Watchdog timer to make sure we're not ruined
  1424. proc trivia_watchdog { } {
  1425. #<<<
  1426. global trivia_last_ts trivia_watchdog_timer trivia_status trivia_channel trivia_delay trivia_speed
  1427. putloglev d * "trivia watchdog: tick"
  1428. # slow down if we've never asked a question or we're stopped
  1429. if {($trivia_last_ts == 0) || ($trivia_status == 0)} {
  1430. putloglev 1 * "trivia is not running, slowing down watchdog"
  1431. set trivia_watchdog_timer [utimer 45 trivia_watchdog]
  1432. return 0
  1433. }
  1434. set current_ts [clock seconds]
  1435. set difference [expr $current_ts - $trivia_last_ts]
  1436. set trivia_limit [expr $trivia_delay + $trivia_speed + 5]
  1437. putloglev 1 * "trivia watchdog: current: $current_ts, last: $trivia_last_ts, difference: $difference, max: $trivia_limit"
  1438. if {$difference > $trivia_limit} {
  1439. if {$trivia_status == 1} {
  1440. putlog "watchdog: trivia is broken"
  1441. putquick "PRIVMSG $trivia_channel :Oops, I think I'm broken. Attempting to recover..."
  1442. set trivia_status 0
  1443. trivia_start
  1444. }
  1445. }
  1446. set timer_interval [expr $trivia_delay * 2]
  1447. set trivia_watchdog_timer [utimer 10 trivia_watchdog]
  1448. return 0
  1449. }
  1450. #>>>
  1451. # Kill the watchdog timeer
  1452. proc trivia_killwatchdog { } {
  1453. #<<<
  1454. global trivia_watchdog_timer
  1455. set alltimers [utimers]
  1456. foreach t $alltimers {
  1457. putloglev 1 * "checking timer $t"
  1458. set t_function [lindex $t 1]
  1459. set t_name [lindex $t 2]
  1460. set t_function [string tolower $t_function]
  1461. if {$t_function == "trivia_watchdog"} {
  1462. putloglev d * "killing timer $t_name"
  1463. killutimer $t_name
  1464. }
  1465. if {$t_function == "trivia_score_rot_timer"} {
  1466. putloglev d * "killing timer $t_name"
  1467. killutimer $t_name
  1468. }
  1469. }
  1470. unset trivia_watchdog_timer
  1471. }
  1472. #>>>
  1473. trivia_killwatchdog
  1474. set trivia_watchdog_timer [utimer 45 trivia_watchdog]
  1475. #>>>
  1476. #<<< SCORE ROTATION STUFF
  1477. #take this week's scores and figure out this week's winners
  1478. proc trivia_score_winners { } {
  1479. #<<<
  1480. global trivia_db_handle trivia_channel
  1481. set winners [list]
  1482. set now [clock seconds]
  1483. set updates [list]
  1484. #knock off a week
  1485. set cutoff [expr $now - 604800]
  1486. set sql "SELECT user_id, COUNT(*) AS score FROM scores WHERE dt > $cutoff GROUP BY user_id ORDER BY score DESC LIMIT 5"
  1487. putloglev d * $sql
  1488. trivia_db_handle eval $sql {
  1489. lappend winners [list $user_id $score]
  1490. }
  1491. set winner_name [list]
  1492. foreach winner $winners {
  1493. set sql "SELECT user_name FROM users WHERE user_id = '[trivia_sqlite_escape [lindex $winner 0]]'"
  1494. putloglev d * $sql
  1495. trivia_db_handle eval $sql {
  1496. lappend winner_name [list $user_name [lindex $winner 0] [lindex $winner 1]]
  1497. }
  1498. }
  1499. putlog $winners
  1500. putlog $winner_name
  1501. putserv "PRIVMSG $trivia_channel :This week's winners are:"
  1502. set position 1
  1503. foreach winner $winner_name {
  1504. #smudge woz ere
  1505. putserv "PRIVMSG $trivia_channel :$position[trivia_ordinal $position] place: [lindex $winner 0] with [lindex $winner 2] points"
  1506. set sql "INSERT INTO winners VALUES ([lindex $winner 1], $now, [expr 6 - $position])"
  1507. putloglev d * $sql
  1508. trivia_db_handle eval $sql
  1509. set sql "UPDATE users SET user_score = user_score + [expr 6 - $position]"
  1510. putloglev d * $sql
  1511. trivia_db_handle eval $sql
  1512. incr position
  1513. }
  1514. set sql "DELETE FROM scores"
  1515. putloglev d * $sql
  1516. trivia_db_handle eval $sql
  1517. }
  1518. #>>>
  1519. #get the timestamp for next cutoff
  1520. proc trivia_score_get_time { } {
  1521. #<<<
  1522. global trivia_score_time
  1523. set trivia_score_time [clock scan "friday 19:00"]
  1524. if {$trivia_score_time < [clock seconds]} {
  1525. set trivia_score_time [clock scan "next friday 19:00"]
  1526. }
  1527. set trivia_score_time [expr $trivia_score_time]
  1528. putloglev d * "setting next score rotation to [clock format $trivia_score_time]"
  1529. }
  1530. #>>>
  1531. #figure out how long is left until the score rotation
  1532. proc trivia_score_time_left { } {
  1533. #<<<
  1534. global trivia_score_time
  1535. set now [clock seconds]
  1536. if {$now > $trivia_score_time} {
  1537. #erk
  1538. putloglev d * "trivia_score_time_left: oops!"
  1539. putloglev d * "now: $now, time: $trivia_score_time"
  1540. putloglev d * [clock format $now]
  1541. putloglev d * [clock format $trivia_score_time]
  1542. return
  1543. }
  1544. set diff [expr $trivia_score_time - $now]
  1545. putloglev d * "trivia_score_time_left: difference is $diff seconds"
  1546. if {$diff > 60} {
  1547. return [trivia_time_to_words $diff]
  1548. } else {
  1549. return "less than a minute"
  1550. }
  1551. }
  1552. #>>>
  1553. proc trivia_time_to_words { time } {
  1554. #<<<
  1555. putloglev 2 * "trivia_time_to_words $time"
  1556. set output ""
  1557. if {$time > 86400} {
  1558. set days [expr $time / 86400]
  1559. append output $days
  1560. append output " day"
  1561. if {$days > 1} {
  1562. append output "s"
  1563. }
  1564. set time [expr $time - [expr $days * 86400]]
  1565. append output " "
  1566. }
  1567. if {$time > 3600} {
  1568. set hours [expr $time / 3600]
  1569. append output $hours
  1570. append output " hour"
  1571. if {$hours > 1} {
  1572. append output "s"
  1573. }
  1574. set time [expr $time - [expr $hours * 3600]]
  1575. append output " "
  1576. }
  1577. if {$time > 60} {
  1578. set mins [expr $time / 60]
  1579. append output $mins
  1580. append output " minute"
  1581. if {$mins > 1} {
  1582. append output "s"
  1583. }
  1584. }
  1585. set output [string trim $output]
  1586. putloglev d * "trivia_time_to_words returing $output"
  1587. return $output
  1588. }
  1589. #>>>
  1590. proc trivia_score_rot_timer { } {
  1591. global trivia_score_time trivia_channel trivia_c trivia_asking_question trivia_warned
  1592. #putloglev 1 * "score_rot tick"
  1593. utimer 10 trivia_score_rot_timer
  1594. if {[clock seconds] > $trivia_score_time} {
  1595. #end of week!
  1596. putlog "trivia: end of week"
  1597. if {$trivia_asking_question == 1} {
  1598. putlog "trivia: game is running"
  1599. if {$trivia_warned == 0} {
  1600. putlog "trivia: need to warn"
  1601. putquick "PRIVMSG $trivia_channel :$trivia_c(red)### BING BONG ###"
  1602. putquick "PRIVMSG $trivia_channel :I've started so I'll finish."
  1603. set trivia_warned 1
  1604. }
  1605. return 0
  1606. } else {
  1607. putlog "trivia: not running game, ending week now"
  1608. trivia_end_week
  1609. return 0
  1610. }
  1611. }
  1612. set trivia_warned 0
  1613. }
  1614. #>>>
  1615. # handle end of week
  1616. proc trivia_end_week { } {
  1617. #<<<
  1618. global trivia_channel trivia_score_time
  1619. # set next week end
  1620. trivia_score_get_time
  1621. # move scores around
  1622. trivia_score_winners
  1623. #>>>
  1624. }
  1625. # Announce the time remaining
  1626. proc trivia_countdown { } {
  1627. #<<<
  1628. global trivia_channel
  1629. putserv "PRIVMSG $trivia_channel :[trivia_score_time_left] left until end of this game."
  1630. }
  1631. #>>>
  1632. trivia_connect
  1633. trivia_score_get_time
  1634. utimer 10 trivia_score_rot_timer
  1635. putlog {TriviaEngine ENGAGED(*$£&($}
  1636. if {$trivia_must_rehash == 2} {
  1637. putlog "Auto-restarting trivia..."
  1638. trivia_start
  1639. set trivia_must_rehash 0
  1640. }