TriviaEngine.tcl 32 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351
  1. # The trivia engine YAY
  2. # vim: foldmethod=marker:foldcolumn=2:foldmarker=<<<,>>>:sw=2:ts=2
  3. ### INIT <<<
  4. # load mysqltcl
  5. package require mysqltcl
  6. # the channel to play in
  7. set trivia_channel "#triviacow"
  8. # the time between hints (sec)
  9. set trivia_speed 20
  10. # the time between rounds (sec)
  11. set trivia_delay 30
  12. # hold the current question info <<<
  13. set trivia_q_id ""
  14. set trivia_q_cat ""
  15. set trivia_q_question ""
  16. set trivia_q_answer ""
  17. set trivia_q_hint ""
  18. set trivia_q_attempts 0
  19. set trivia_unanswered 0
  20. set trivia_run_last 0
  21. set trivia_run_qty 0
  22. set trivia_run_nick ""
  23. set trivia_guesser ""
  24. set trivia_guesser_count 0
  25. set trivia_last_qid 0
  26. if [info exists trivia_must_rehash] {
  27. if {$trivia_must_rehash == 1} {
  28. set trivia_must_rehash 2
  29. } else {
  30. set trivia_must_rehash 0
  31. }
  32. } else {
  33. set trivia_must_rehash 0
  34. }
  35. #>>>
  36. # 0 = off
  37. # 1 = on
  38. # -1 = disabled
  39. set trivia_status 0
  40. set trivia_timer ""
  41. set trivia_watchdog_timer ""
  42. set trivia_last_ts 0
  43. #colours <<<
  44. set trivia_c(off) "\003"
  45. set trivia_c(red) "\0034"
  46. set trivia_c(blue) "\0033"
  47. set trivia_c(purple) "\0036"
  48. set trivia_c(bold) "\002"
  49. #>>>
  50. bind pubm - * trivia_input
  51. bind msg - trivia trivia_msg
  52. # connect mysql
  53. set trivia_db_handle [mysqlconnect -host localhost -user root -password 7MKh3ako -db trivialive]
  54. #>>>
  55. ### SETTINGS <<<
  56. set trivia_flag T
  57. set trivia_admin S
  58. # >>>
  59. # Handle a /msg
  60. proc trivia_msg { nick host handle cmd } {
  61. #<<<
  62. global trivia_db_handle trivia_last_qid
  63. global trivia_q_cat trivia_c
  64. regsub "(trivia )" $cmd "" cmd
  65. putlog "trivia: msg command $cmd from $nick"
  66. if {($nick == "Greeneyez") || ($nick == "JamesOff")} {
  67. if [regexp -nocase "^setcat (.+)" $cmd matches category] {
  68. if {$trivia_last_qid == 0} {
  69. puthelp "PRIVMSG $nick :Can't fathom last question, unable to update category"
  70. return
  71. }
  72. set orig_cat $category
  73. set category [mysqlescape $category]
  74. set sql "SELECT cat_id FROM categories WHERE cat_name='$category'"
  75. set result [mysqlquery $trivia_db_handle $sql]
  76. if {[set row [mysqlnext $result]] != ""} {
  77. set cat_id [lindex $row 0]
  78. mysqlendquery $result
  79. puthelp "PRIVMSG $nick :Moving question $trivia_last_qid to category$trivia_c(purple) $category$trivia_c(off) ($cat_id)"
  80. set sql "UPDATE questions SET cat_id='$cat_id' WHERE question_id='$trivia_last_qid'"
  81. mysqlexec $trivia_db_handle $sql
  82. set trivia_q_cat $orig_cat
  83. return
  84. } else {
  85. puthelp "PRIVMSG $nick :Creating new category$trivia_c(purple) $category"
  86. set sql "INSERT INTO categories VALUES (null, '$category', 1)"
  87. mysqlexec $trivia_db_handle $sql
  88. set cat_id [mysqlinsertid $trivia_db_handle]
  89. puthelp "PRIVMSG $nick :Moving question $trivia_last_qid to category$trivia_c(purple) $category$trivia_c(off) ($cat_id)"
  90. set sql "UPDATE questions SET cat_id='$cat_id' WHERE question_id='$trivia_last_qid'"
  91. mysqlexec $trivia_db_handle $sql
  92. set trivia_q_cat $orig_cat
  93. return
  94. }
  95. }
  96. }
  97. }
  98. #>>>
  99. # Handle input coming from a channel
  100. proc trivia_input { nick host handle channel arg } {
  101. #<<<
  102. global trivia_channel trivia_status trivia_q_answer
  103. global trivia_guesser_count trivia_guesser trivia_c
  104. if {[string tolower $trivia_channel] != [string tolower $channel]} {
  105. return 0
  106. }
  107. if [regexp -nocase "^!t(rivia)? (.+)" $arg matches cmd param] {
  108. trivia_command $nick $host $handle $channel $param
  109. return 0
  110. }
  111. if [regexp -nocase "^!t(rivia)?$" $arg] {
  112. if {$trivia_status == 1} {
  113. puthelp "PRIVMSG $trivia_channel :!trivia ... ?"
  114. return 0
  115. } else {
  116. puthelp "PRIGMSG $trivia_channel :Perhaps you want $trivia_c(purple)!trivia start"
  117. return 0
  118. }
  119. }
  120. # need to be running below here
  121. if {$trivia_status == -1} {
  122. return 0
  123. }
  124. if {$trivia_q_answer == ""} {
  125. return 0
  126. }
  127. #see if someone else is playing
  128. if {($nick == $trivia_guesser) && ($nick != "NoTopic")} {
  129. incr trivia_guesser_count
  130. putloglev d * "$nick has talked $trivia_guesser_count times in a row..."
  131. } else {
  132. set trivia_guesser_count 0
  133. set trivia_guesser $nick
  134. }
  135. #tidy the string up
  136. set arg [string tolower $arg]
  137. set arg [string trim $arg]
  138. if {$arg == [string tolower $trivia_q_answer]} {
  139. trivia_correct $nick
  140. return 0
  141. }
  142. #if they didn't get it right, and it's been more than 20 lines, taunt!
  143. if {($trivia_guesser_count > 0) && (($trivia_guesser_count % 20)) == 0} {
  144. putserv "PRIVMSG $trivia_channel :Playing with yourself, $nick? ;)"
  145. putlog "Is $nick playing with themselves?"
  146. }
  147. }
  148. #>>>
  149. # Someone got it right!
  150. proc trivia_correct { nick } {
  151. #<<<
  152. global trivia_q_id trivia_q_cat trivia_q_question trivia_q_answer trivia_q_hint trivia_q_attempts trivia_channel trivia_status
  153. global trivia_timer trivia_delay trivia_db_handle trivia_unanswered trivia_run_qty trivia_run_last trivia_run_nick trivia_c
  154. if {$trivia_status != 1} {
  155. #something strange going on
  156. return 0
  157. }
  158. trivia_killtimer
  159. set answer $trivia_q_answer
  160. set trivia_q_answer ""
  161. set newuser 0
  162. set uid [trivia_get_uid $nick]
  163. if {$uid == 0} {
  164. putlog "$nick does not have entry in database... creating"
  165. set uid [trivia_create_user $nick]
  166. set newuser 1
  167. }
  168. putlog "$nick has uid $uid"
  169. trivia_incr_score $uid
  170. putquick "PRIVMSG $trivia_channel :Congratulations $trivia_c(purple)$nick$trivia_c(off)! The answer was$trivia_c(purple) $answer$trivia_c(off)."
  171. if {$newuser == 1} {
  172. putquick "PRIVMSG $trivia_channel :Welcome to our newest player, $trivia_c(purple)$nick$trivia_c(off) :)"
  173. }
  174. putquick "PRIVMSG $trivia_channel :rank0rs: [trivia_near_five2 $uid]"
  175. #set score [trivia_get_score $uid]
  176. #putserv "PRIVMSG $trivia_channel :$nick now has $score points."
  177. set trivia_timer [utimer $trivia_delay trivia_start_round]
  178. set trivia_unanswered 0
  179. if {$trivia_run_last == $uid} {
  180. incr trivia_run_qty
  181. if {$trivia_run_qty > 2} {
  182. putquick "PRIVMSG $trivia_channel :$nick [trivia_get_run $trivia_run_qty] $trivia_run_qty in a row!"
  183. }
  184. } else {
  185. #end of streak
  186. if {$trivia_run_qty > 2} {
  187. putquick "PRIVMSG $trivia_channel :$nick ended $trivia_run_nick's winning spree."
  188. }
  189. set trivia_run_qty 1
  190. set trivia_run_last $uid
  191. set trivia_run_nick $nick
  192. }
  193. if {[rand 100] > 95} {
  194. putserv "PRIVMSG $trivia_channel :Remember, to report a problem with a question or answer, type $trivia_c(purple)!trivia report <description of problem>"
  195. }
  196. trivia_check_rehash
  197. }
  198. #>>>
  199. #See if we need to rehash
  200. proc trivia_check_rehash { } {
  201. #<<<
  202. global trivia_must_rehash trivia_channel trivia_c
  203. if {$trivia_must_rehash == 1} {
  204. putquick "PRIVMSG $trivia_channel :$trivia_c(red)### Please wait, reloading trivia script ###"
  205. trivia_killtimer
  206. rehash
  207. }
  208. }
  209. #>>>
  210. # Turn a winning spree into text
  211. proc trivia_get_run { row } {
  212. #<<<
  213. switch $row {
  214. 3 {
  215. return "is on a winning spree!"
  216. }
  217. 5 {
  218. return "is on a rampage!"
  219. }
  220. 6 {
  221. return "is unstoppable!"
  222. }
  223. 8 {
  224. return "is on a Google spree!"
  225. }
  226. 10 {
  227. return "is GODLIKE!"
  228. }
  229. 12 {
  230. return "needs to get out more."
  231. }
  232. }
  233. return "is on a roll ..."
  234. }
  235. #>>>
  236. # Get someone's user ID from their nick
  237. proc trivia_get_uid { nick } {
  238. #<<<
  239. global trivia_db_handle
  240. putloglev 4 * "trivia_get_uid ($nick)"
  241. set nick [mysqlescape $nick]
  242. set sql "SELECT user_id FROM users WHERE user_name = '$nick'"
  243. set result [mysqlquery $trivia_db_handle $sql]
  244. #putlog $sql
  245. if {[set row [mysqlnext $result]] != ""} {
  246. set user_id [lindex $row 0]
  247. mysqlendquery $result
  248. return $user_id
  249. } else {
  250. return 0
  251. }
  252. }
  253. #>>>
  254. # Get a period
  255. proc trivia_get_period { } {
  256. #<<<
  257. return [expr [clock scan "last friday 23:59" -gmt true] + 60]
  258. }
  259. #>>>
  260. # Get a score from a UID
  261. proc trivia_get_score { user_id } {
  262. #<<<
  263. global trivia_db_handle
  264. putloglev 4 * "trivia_get_score ($user_id)"
  265. set dt [trivia_get_period]
  266. set sql "SELECT COUNT(*) AS yarr FROM scores WHERE dt > '$dt' AND user_id='$user_id'"
  267. set result [mysqlquery $trivia_db_handle $sql]
  268. if {[set row [mysqlnext $result]] != ""} {
  269. set score [lindex $row 0]
  270. mysqlendquery $result
  271. return $score
  272. } else {
  273. return 0
  274. }
  275. }
  276. #>>>
  277. # Turn a timestamp into a date
  278. proc trivia_ts_to_date { ts } {
  279. #<<<
  280. return [clock format $ts -format "%Y/%m/%d %H:%M"]
  281. }
  282. #>>>
  283. # Get someone's userinfo from their UID
  284. proc trivia_get_userinfo { user_id } {
  285. #<<<
  286. global trivia_db_handle
  287. putloglev 4 * "trivia_get_userinfo ($user_id)"
  288. set sql "SELECT user_score, user_last, user_reg FROM users WHERE user_id = '$user_id'"
  289. set result [mysqlquery $trivia_db_handle $sql]
  290. if {[set row [mysqlnext $result]] != ""} {
  291. set last [lindex $row 1]
  292. set score [trivia_get_score $user_id]
  293. if {$last == ""} {
  294. set last "Unknown"
  295. } else {
  296. set last [trivia_ts_to_date $last]
  297. }
  298. set reg [lindex $row 2]
  299. if {$reg == ""} {
  300. set reg "Unkown"
  301. } else {
  302. set reg [trivia_ts_to_date $reg]
  303. }
  304. return "$score|$last|$reg"
  305. mysqlendquery $result
  306. } else {
  307. return "No stats"
  308. }
  309. }
  310. #>>>
  311. # Get someone's stats from their username
  312. proc trivia_user_stats { user_name } {
  313. #<<<
  314. global trivia_channel
  315. set uid [trivia_get_uid $user_name]
  316. if {$uid == 0} {
  317. putserv "PRIVMSG $trivia_channel :Unknown user '$user_name'"
  318. return
  319. }
  320. set stats [trivia_get_userinfo $uid]
  321. if {$stats == "No stats"} {
  322. putserv "PRIVMSG $trivia_channel :No stats available."
  323. return
  324. }
  325. set stats2 [split $stats "|"]
  326. putserv "PRIVMSG $trivia_channel :Trivia stats for $user_name:"
  327. putserv "PRIVMSG $trivia_channel : Current score: [lindex $stats2 0]"
  328. putserv "PRIVMSG $trivia_channel : Ranking: [trivia_get_rank $uid]"
  329. putserv "PRIVMSG $trivia_channel : First seen: [lindex $stats2 2]"
  330. putserv "PRIVMSG $trivia_channel : Last scored: [lindex $stats2 1]"
  331. }
  332. #>>>
  333. # Increase a UID's score
  334. proc trivia_incr_score { id { howmuch 1 } } {
  335. #<<<
  336. global trivia_db_handle
  337. putloglev 4 * "trivia_incr_score ($id, $howmuch)"
  338. #set sql "UPDATE users SET user_score = user_score + $howmuch WHERE user_id = '$id'"
  339. #mysqlexec $trivia_db_handle $sql
  340. set sql "UPDATE users SET user_last = UNIX_TIMESTAMP() WHERE user_id = '$id'"
  341. mysqlexec $trivia_db_handle $sql
  342. set sql "INSERT INTO scores VALUES ('$id', '[clock seconds]')"
  343. mysqlexec $trivia_db_handle $sql
  344. }
  345. #>>>
  346. # Create a new user
  347. proc trivia_create_user { nick } {
  348. #<<<
  349. global trivia_db_handle
  350. putloglev 4 * "trivia_create_user ($nick)"
  351. set nick [mysqlescape $nick]
  352. set sql "INSERT INTO users VALUES (null, '$nick', '', 0, UNIX_TIMESTAMP(), UNIX_TIMESTAMP())"
  353. mysqlexec $trivia_db_handle $sql
  354. set uid [mysqlinsertid $trivia_db_handle]
  355. return $uid
  356. }
  357. #>>>
  358. # Handle a !trivia command
  359. proc trivia_command { nick host handle channel param } {
  360. #<<<
  361. global trivia_channel trivia_status trivia_flag trivia_admin trivia_c
  362. set arg ""
  363. regexp {^([^ ]+)( .+)?$} $param matches cmd arg
  364. set param [string tolower $cmd]
  365. set arg [string trim $arg]
  366. putloglev d * "trivia command: $param from: $nick ($arg)"
  367. if {[matchattr $handle |$trivia_admin $trivia_channel] && ($param != "enable") && ($trivia_status == -1)} {
  368. return 0
  369. }
  370. if [regexp -nocase "^(score|place)( .+)?" $param] {
  371. putserv "PRIVMSG $trivia_channel :[trivia_score $arg $nick]"
  372. if [string match -nocase $nick $arg] {
  373. putserv "PRIVMSG $trivia_channel :(You know putting your own nick is optional, right? :)"
  374. }
  375. return 0
  376. }
  377. if {$param == "top10"} {
  378. putserv "PRIVMSG $trivia_channel :[trivia_get_top10]"
  379. return 0
  380. }
  381. if {$param == "info"} {
  382. trivia_user_stats $arg
  383. return 0
  384. }
  385. if {$param == "start"} {
  386. trivia_start
  387. return 0
  388. }
  389. if {$param == "report"} {
  390. trivia_report $nick $arg
  391. return 0
  392. }
  393. if {![matchattr $handle |$trivia_flag $channel]} {
  394. putserv "PRIVMSG $nick :use: !trivia \[score|top10|start\]"
  395. return 0
  396. }
  397. if {$param == "stop"} {
  398. trivia_stop
  399. return 0
  400. }
  401. if {$param == "skip"} {
  402. trivia_skip $nick
  403. return 0
  404. }
  405. if {$param == "stats"} {
  406. trivia_stats
  407. return 0
  408. }
  409. if {![matchattr $handle |$trivia_admin $channel]} {
  410. putserv "PRIVMSG $nick :use: !trivia \[score|top10|start|stop|skip|stats\]"
  411. return 0
  412. }
  413. if {$param == "disable"} {
  414. trivia_disable
  415. return 0
  416. }
  417. if {$param == "enable"} {
  418. trivia_enable
  419. return 0
  420. }
  421. if {$param == "merge"} {
  422. trivia_merge $nick $arg
  423. #puthelp "PRIGMSG $trivia_channel :Score merging is disabled"
  424. return 0
  425. }
  426. if {$param == "rehash"} {
  427. trivia_rehash
  428. return 0
  429. }
  430. putserv "PRIVMSG $nick :use: !trivia \[start|stop|score|top10|skip|stats|enable|disable|merge\]"
  431. return 0
  432. }
  433. #>>>
  434. # Rehash
  435. proc trivia_rehash { } {
  436. #<<<
  437. global trivia_status trivia_channel trivia_must_rehash
  438. if {$trivia_must_rehash == 1} {
  439. putserv "PRIVMSG $trivia_channel :! Reloading trivia now..."
  440. rehash
  441. return 0
  442. }
  443. if {$trivia_status != 1} {
  444. putserv "PRIVMSG $trivia_channel :! Reloading trivia now..."
  445. rehash
  446. return 0
  447. }
  448. set trivia_must_rehash 1
  449. putserv "PRIVMSG $trivia_channel :Trivia will reload after current round"
  450. }
  451. #>>>
  452. # Disable the script
  453. proc trivia_disable { } {
  454. #<<<
  455. global trivia_status trivia_channel
  456. if {$trivia_status == 1} {
  457. #stop the current game
  458. trivia_stop
  459. }
  460. set trivia_status -1
  461. putserv "PRIVMSG $trivia_channel :Trivia disabled."
  462. return 0
  463. }
  464. #>>>
  465. # Enable the script1
  466. proc trivia_enable {} {
  467. #<<<
  468. global trivia_status trivia_channel
  469. set trivia_status 0
  470. putserv "PRIVMSG $trivia_channel :Trivia enabled."
  471. return 0
  472. }
  473. #>>>
  474. #Make a hint
  475. proc trivia_make_hint { hint answer } {
  476. #<<<
  477. set hint [string toupper $hint]
  478. set answer [string toupper $answer]
  479. set answer_words [split $answer { }]
  480. set final_hint ""
  481. #are we making the very first hint?
  482. if {$hint == ""} {
  483. foreach word $answer_words {
  484. append final_hint [string repeat "_" [string length $word]]
  485. append final_hint " "
  486. }
  487. set final_hint [string trim $final_hint]
  488. #now put the punctuation in
  489. set letters [split $answer {}]
  490. set i 0
  491. foreach letter $letters {
  492. if {![regexp -nocase {[A-Z0-9 ]} $letter]} {
  493. set final_hint [string replace $final_hint $i $i $letter]
  494. }
  495. incr i
  496. }
  497. return [string trim $final_hint]
  498. }
  499. # explode the into words
  500. set hint_words [split $hint { }]
  501. set i 0
  502. foreach word $hint_words {
  503. set answer_word [lindex $answer_words $i]
  504. putloglev 1 * "considering $answer_word ($word)"
  505. #are we on the first iteration?
  506. if [regexp "^_+$" $word] {
  507. #use the first letter
  508. set letters [string length $word]
  509. #don't hint for single-letter words
  510. if {$letters > 1} {
  511. set word [string index $answer_word 0]
  512. append word [string repeat "_" [expr $letters - 1]]
  513. } else {
  514. set word "_"
  515. }
  516. append final_hint "$word "
  517. } else {
  518. #not the first iteration so figure out where the gaps are
  519. set gaps [list]
  520. set letters [split $word {}]
  521. set j 0
  522. foreach letter $letters {
  523. if {$letter == "_"} {
  524. lappend gaps $j
  525. }
  526. incr j
  527. }
  528. if {[llength $gaps] <= 1} {
  529. #eek no letters to replace, or only one gap
  530. putloglev 1 * " insufficient gaps, using $word"
  531. append final_hint "$word "
  532. } else {
  533. #now we pick a random letter position
  534. set pos [trivia_random_element $gaps]
  535. putloglev 1 * " filling in $pos"
  536. set word [string replace $word $pos $pos [string index $answer_word $pos]]
  537. putloglev 1 * " --> $word"
  538. append final_hint "$word "
  539. }
  540. }
  541. incr i
  542. }
  543. set final_hint [string trim $final_hint]
  544. return $final_hint
  545. }
  546. #>>>
  547. # Fetch a question
  548. proc trivia_get_question { } {
  549. #<<<
  550. global trivia_db_handle trivia_q_id trivia_q_cat trivia_q_question trivia_q_answer trivia_q_hint
  551. 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, rand() LIMIT 1"
  552. set result [mysqlquery $trivia_db_handle $sql]
  553. if {[set row [mysqlnext $result]] != ""} {
  554. set trivia_q_id [lindex $row 1]
  555. set trivia_q_cat [lindex $row 3]
  556. set trivia_q_question [lindex $row 0]
  557. set trivia_q_answer [string toupper [lindex $row 2]]
  558. set trivia_q_hint ""
  559. mysqlendquery $result
  560. } else {
  561. putlog "ERROR: Couldn't fetch a question from the database."
  562. }
  563. #update the times used
  564. set trivia_q_id [mysqlescape $trivia_q_id]
  565. set sql "UPDATE questions SET count = count + 1 WHERE question_id = '$trivia_q_id'"
  566. mysqlexec $trivia_db_handle $sql
  567. }
  568. #>>>
  569. # Start a round
  570. proc trivia_start_round { } {
  571. #<<<
  572. 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
  573. if {$trivia_status != 1} {
  574. #we're switched off, abort
  575. return 0
  576. }
  577. #init variables
  578. set trivia_q_id ""
  579. set trivia_q_cat ""
  580. set trivia_q_question ""
  581. set trivia_q_answer ""
  582. set trivia_q_hint ""
  583. putlog "Fetching next question..."
  584. trivia_get_question
  585. if {$trivia_q_id == ""} {
  586. putlog "Couldn't init trivia round, aborting."
  587. return
  588. }
  589. putlog "Successfully fetched question $trivia_q_id from database"
  590. putloglev 4 * "question = $trivia_q_question"
  591. putloglev 4 * "answer = $trivia_q_answer"
  592. set trivia_q_attempts 1
  593. set trivia_last_qid $trivia_q_id
  594. trivia_round
  595. }
  596. #>>>
  597. # Run a round
  598. proc trivia_round { } {
  599. #<<<
  600. global trivia_q_id trivia_q_cat trivia_q_question trivia_q_answer trivia_q_hint trivia_q_attempts trivia_channel trivia_status
  601. global trivia_timer trivia_speed trivia_c trivia_last_ts
  602. if {$trivia_status != 1} {
  603. #we're switched off, abort
  604. return 0
  605. }
  606. if {$trivia_q_attempts == 5} {
  607. trivia_end_round
  608. return 0
  609. }
  610. if {$trivia_q_answer == ""} {
  611. return 0
  612. }
  613. #update the hint
  614. set trivia_q_hint [trivia_make_hint $trivia_q_hint $trivia_q_answer]
  615. #say our stuff
  616. if {$trivia_q_attempts > 1} {
  617. set hint " \[[expr $trivia_q_attempts - 1] of 3\]"
  618. } else {
  619. set hint ""
  620. }
  621. putserv "PRIVMSG $trivia_channel :$trivia_c(red)--== Trivia ==--$trivia_c(off) \[category: \002$trivia_q_cat\002\]"
  622. #\[question id: \002$trivia_q_id\002\]"
  623. set split_question [trivia_question_split $trivia_q_question]
  624. foreach q $split_question {
  625. if {$q != ""} {
  626. putserv "PRIVMSG $trivia_channel :$trivia_c(blue) [trivia_question_inject $q]"
  627. }
  628. }
  629. #set new_question [trivia_question_inject $trivia_q_question]
  630. #putserv "PRIVMSG $trivia_channel :$trivia_c(blue) $new_question"
  631. putserv "PRIVMSG $trivia_channel :Hint$hint: [trivia_explode $trivia_q_hint]"
  632. incr trivia_q_attempts
  633. set trivia_last_ts [clock seconds]
  634. global trivia_must_rehash
  635. if {$trivia_must_rehash != 2} {
  636. set trivia_timer [utimer $trivia_speed trivia_round]
  637. }
  638. }
  639. #>>>
  640. # Make it harder for things to break questions (inject bold codes)
  641. proc trivia_question_inject { question } {
  642. #<<<
  643. putlog "trivia_question_inject $question"
  644. #inject random bold/underline/colour codes into question
  645. global trivia_c
  646. set l [string length $question]
  647. putlog "length: $l"
  648. if {$l < 1} {
  649. return $question
  650. }
  651. set pos [rand $l]
  652. set first [string range $question 0 $pos]
  653. incr pos
  654. set second [string range $question $pos end]
  655. switch [rand 1] {
  656. 0 {
  657. putlog "question_inject: using bold at pos $pos"
  658. return $first$trivia_c(bold)$trivia_c(bold)$second
  659. }
  660. 1 {
  661. putlog "question_inject: using purple at pos $pos"
  662. return $first$trivia_c(purple)$trivia_c(off)$second
  663. }
  664. }
  665. return $question
  666. }
  667. #>>>
  668. # Make it harder for things to break questions (inject line breaks)
  669. proc trivia_question_split { question } {
  670. #<<<
  671. putlog "trivia_question_split $question"
  672. #explodes a question into two lines at word boundaries, if it's long enough
  673. #don't split unscrambles incorrectly
  674. if [regexp -nocase "(unscramble .+:) (\[A-Z \]+)" $question matches first second] {
  675. return [list $first $second]
  676. }
  677. set words [split $question " "]
  678. set wordcount [llength $words]
  679. if {$wordcount < 4} {
  680. putlog "aborting, too short"
  681. return [list $question]
  682. }
  683. #enough words to split
  684. #we want at least two on the first line
  685. putlog "wordcount: $wordcount"
  686. incr wordcount -2
  687. if {$wordcount < 0} {
  688. return [list $question]
  689. }
  690. set pos [rand $wordcount]
  691. incr pos 2
  692. putlog "picked pos $pos"
  693. set wordlist [list]
  694. set i 0
  695. set line ""
  696. foreach word $words {
  697. #putlog "word = $word, i = $i"
  698. append line "$word "
  699. if {$i == $pos} {
  700. #putlog "splitting here, line = $line"
  701. lappend wordlist [string trim $line]
  702. set line ""
  703. }
  704. incr i
  705. }
  706. #putlog "done, appending $line to list"
  707. if {$line != ""} {
  708. lappend wordlist [string trim $line]
  709. }
  710. #putlog "split question list is: $wordlist"
  711. return $wordlist
  712. }
  713. #>>>
  714. # Finish a round (without a winner)
  715. proc trivia_end_round { } {
  716. #<<<
  717. global trivia_q_id trivia_q_cat trivia_q_question trivia_q_answer trivia_q_hint trivia_q_attempts trivia_channel trivia_status
  718. global trivia_timer trivia_delay trivia_db_handle trivia_unanswered
  719. global trivia_run_last trivia_run_nick trivia_run_qty trivia_c
  720. if {$trivia_status != 1} {
  721. #we're switched off, abort
  722. return 0
  723. }
  724. set trivia_q_answer [string toupper $trivia_q_answer]
  725. putquick "PRIVMSG $trivia_channel :Time's up! Nobody got it right. The answer was$trivia_c(purple) $trivia_q_answer"
  726. set trivia_q_answer ""
  727. incr trivia_unanswered
  728. if {$trivia_run_qty > 2} {
  729. putserv "PRIVMSG $trivia_channel :So much for $trivia_run_nick's winning spree."
  730. }
  731. set trivia_run_last 0
  732. set trivia_run_qty 0
  733. set trivia_run_nick ""
  734. if {[rand 100] > 90} {
  735. putserv "PRIVMSG $trivia_channel :Remember, to report a problem with a question or answer, type $trivia_c(purple)!trivia report <description of problem>"
  736. }
  737. if {$trivia_unanswered > 3} {
  738. putserv "PRIVMSG $trivia_channel :Three unanswered in a row, stopping the game."
  739. set trivia_status 0
  740. } else {
  741. set trivia_timer [utimer $trivia_delay trivia_start_round]
  742. trivia_check_rehash
  743. }
  744. }
  745. #>>>
  746. # Skip the rest of this question
  747. proc trivia_skip { nick } {
  748. #<<<
  749. global trivia_q_id trivia_q_cat trivia_q_question trivia_q_answer trivia_q_hint trivia_q_attempts trivia_channel trivia_status
  750. global trivia_timer trivia_delay trivia_db_handle trivia_unanswered
  751. if {$trivia_status != 1} {
  752. #we're switched off, abort
  753. return 0
  754. }
  755. if {$trivia_q_answer == ""} {
  756. #no round in progress
  757. return 0
  758. }
  759. set trivia_q_question ""
  760. set trivia_q_answer ""
  761. trivia_killtimer
  762. putserv "PRIVMSG $trivia_channel :Skipping this question by $nick's request."
  763. set trivia_timer [utimer $trivia_delay trivia_start_round]
  764. }
  765. #>>>
  766. # Utility function to fetch a random item from a list
  767. proc trivia_random_element { l } {
  768. #<<<
  769. return [lindex $l [rand [llength $l]]]
  770. }
  771. #>>>
  772. # Start the game
  773. proc trivia_start { } {
  774. #<<<
  775. global trivia_status trivia_channel trivia_unanswered trivia_run_last trivia_run_qty trivia_c trivia_must_rehash
  776. if {$trivia_status == 1} {
  777. putserv "PRIVMSG $trivia_channel :Trivia is already running."
  778. return 0
  779. }
  780. if {$trivia_status == -1} {
  781. putserv "PRIVMSG $trivia_channel :Trivia is currently disabled."
  782. return 0
  783. }
  784. # go go go
  785. set trivia_status 1
  786. if {$trivia_must_rehash == 2 } {
  787. putquick "PRIVMSG $trivia_channel :$trivia_c(blue)### Resuming trivia game ###" -next
  788. } else {
  789. putquick "PRIVMSG $trivia_channel :Trivia game started!" -next
  790. }
  791. #trivia_stats
  792. set trivia_unanswered 0
  793. set trivia_run_last 0
  794. set trivia_run_qty 0
  795. trivia_start_round
  796. return 0
  797. }
  798. #>>>
  799. # Stop the game
  800. proc trivia_stop { } {
  801. #<<<
  802. global trivia_channel trivia_status
  803. if {$trivia_status == 1} {
  804. putserv "PRIVMSG $trivia_channel :Trivia game stopped."
  805. set trivia_status 0
  806. trivia_killtimer
  807. }
  808. return 0
  809. }
  810. #>>>
  811. # Kill our timer
  812. proc trivia_killtimer { } {
  813. #<<<
  814. global trivia_timer
  815. if {$trivia_timer != ""} {
  816. killutimer $trivia_timer
  817. }
  818. }
  819. #>>>
  820. # Utility function to explode line into letters
  821. proc trivia_explode { line } {
  822. #<<<
  823. set letters [split $line {}]
  824. set newline ""
  825. foreach letter $letters {
  826. append newline "$letter "
  827. }
  828. return [string trim $newline]
  829. }
  830. #>>>
  831. # Get a UID's ranking
  832. proc trivia_get_rank { uid } {
  833. #<<<
  834. global trivia_db_handle
  835. putloglev 4 * "trivia_get_rank ($uid)"
  836. set sql "SELECT user_id FROM users ORDER by user_score DESC"
  837. set dt [trivia_get_period]
  838. set sql "select user_id, count(dt) as d from scores where dt > $dt group by user_id order by d desc"
  839. set result [mysqlsel $trivia_db_handle $sql -flatlist]
  840. set pos [lsearch -exact $result $uid]
  841. return [expr $pos + 1]
  842. }
  843. #>>>
  844. # Get the top 10 users
  845. proc trivia_get_top10 { } {
  846. #<<<
  847. global trivia_db_handle
  848. set sql "SELECT user_name, user_score FROM users ORDER BY user_score DESC LIMIT 10"
  849. set dt [trivia_get_period]
  850. set sql "select users.user_name, 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"
  851. set result [mysqlsel $trivia_db_handle $sql -list]
  852. set output ""
  853. set index 0
  854. foreach place $result {
  855. incr index
  856. append output "\002$index:\002 "
  857. append output [lindex $place 0]
  858. append output " ("
  859. append output [lindex $place 1]
  860. append output ") "
  861. }
  862. set output "The top $index players are... $output"
  863. return $output
  864. }
  865. #>>>
  866. # Fix a number into text
  867. proc trivia_ordinal { number } {
  868. #<<<
  869. if [regexp {1[0-9]$} $number] {
  870. return "th"
  871. }
  872. set last [string range $number end end]
  873. if {$last == "1"} {
  874. return "st"
  875. }
  876. if {$last == "2"} {
  877. return "nd"
  878. }
  879. if {$last == "3"} {
  880. return "rd"
  881. }
  882. return "th"
  883. }
  884. #>>>
  885. # Get a score
  886. proc trivia_score { n { nick "" } } {
  887. #<<<
  888. set n [string trim $n]
  889. putloglev 4 * "trivia_score($n, $nick)"
  890. if {$n == ""} {
  891. set ni $nick
  892. set o "You are"
  893. } else {
  894. set ni $n
  895. set o "$n is"
  896. }
  897. set uid [trivia_get_uid $ni]
  898. set score [trivia_get_score $uid]
  899. set pos [trivia_get_rank $uid]
  900. if {$score == 0} {
  901. return "No score found for $n."
  902. }
  903. return "$o ranked $pos[trivia_ordinal $pos] with $score points."
  904. }
  905. #>>>
  906. # Get the scores around a UID
  907. proc trivia_near_five { uid } {
  908. #<<<
  909. global trivia_db_handle trivia_c
  910. set sql "SELECT user_id, user_name, user_score FROM users ORDER BY user_score DESC"
  911. set dt [trivia_get_period]
  912. set sql "select users.user_id, users.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 user_score desc"
  913. set result [mysqlsel $trivia_db_handle $sql -flatlist]
  914. set pos [lsearch -exact $result $uid]
  915. #we use +- 6 here because it's a flat list, and we want 2 nodes either side
  916. putlog $result
  917. set near_five [lrange $result [expr $pos - 6] [expr $pos + 8]]
  918. putlog $near_five
  919. set low_pos [expr round(($pos-1) / 3)]
  920. if {$low_pos < 1} {
  921. set low_pos 1
  922. }
  923. putlog $low_pos
  924. set i 0
  925. set output ""
  926. while {$i < [llength $near_five]} {
  927. set user_id [lindex $near_five $i]
  928. incr i
  929. set nick [lindex $near_five $i]
  930. incr i
  931. set score [lindex $near_five $i]
  932. incr i
  933. if {$uid == $user_id} {
  934. append output "$trivia_c(purple)$trivia_c(bold)"
  935. }
  936. append output "$low_pos[trivia_ordinal $low_pos]:"
  937. append output "$nick"
  938. append output " ($score) "
  939. if {$uid == $user_id} {
  940. append output "$trivia_c(off)$trivia_c(bold)"
  941. }
  942. incr low_pos
  943. }
  944. return $output
  945. }
  946. #>>>
  947. # Turn a list into a stats list
  948. proc trivia_list_to_stats { pos user_id l } {
  949. #<<<
  950. putloglev 4 * "trivia_list_to_stats ($pos, $user_id, $l)"
  951. global trivia_c
  952. set uid [lindex $l 0]
  953. set nick [lindex $l 1]
  954. set score [lindex $l 2]
  955. incr pos
  956. set result ""
  957. if {$user_id == $uid} {
  958. set result "$trivia_c(purple)$trivia_c(bold)"
  959. }
  960. append result "$pos[trivia_ordinal $pos]: $nick ($score) "
  961. if {$user_id == $uid} {
  962. append result "$trivia_c(off)$trivia_c(bold)"
  963. }
  964. return $result
  965. }
  966. #>>>
  967. # Another function to get the nearest users to your score
  968. proc trivia_near_five2 { uid } {
  969. #<<<
  970. global trivia_db_handle trivia_c
  971. set sql "SELECT user_id, user_name, user_score FROM users ORDER BY user_score DESC"
  972. set dt [trivia_get_period]
  973. set sql "select users.user_id, users.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 user_score desc"
  974. set result [mysqlsel $trivia_db_handle $sql -list]
  975. putloglev d * "current score list: $result"
  976. set position 0
  977. foreach item $result {
  978. if {[lindex $item 0] == $uid} {
  979. putloglev d * "found at position $position"
  980. break
  981. }
  982. incr position
  983. }
  984. set line ""
  985. if {$position > 2} {
  986. append line [trivia_list_to_stats [expr $position - 2] $uid [lindex $result [expr $position - 2]]]
  987. }
  988. if {$position > 1} {
  989. append line [trivia_list_to_stats [expr $position - 1] $uid [lindex $result [expr $position - 1]]]
  990. }
  991. append line [trivia_list_to_stats $position $uid [lindex $result $position]]
  992. if {$position < [expr [llength $result] - 1]} {
  993. append line [trivia_list_to_stats [expr $position + 1] $uid [lindex $result [expr $position + 1]]]
  994. }
  995. if {$position < [expr [llength $result] - 2]} {
  996. append line [trivia_list_to_stats [expr $position + 2] $uid [lindex $result [expr $position + 2]]]
  997. }
  998. return $line
  999. }
  1000. #>>>
  1001. # Get some stats from the DB
  1002. proc trivia_stats { } {
  1003. #<<<
  1004. global trivia_db_handle trivia_channel
  1005. set sql "SELECT COUNT(*) AS total FROM questions LEFT JOIN categories USING (cat_id) WHERE categories.cat_enabled = 1;"
  1006. set result [mysqlsel $trivia_db_handle $sql -flatlist]
  1007. set stat_total_questions [lindex $result 0]
  1008. set sql "SELECT COUNT(*) AS total FROM categories WHERE cat_enabled = 1"
  1009. set result [mysqlsel $trivia_db_handle $sql -flatlist]
  1010. set stat_total_cats [lindex $result 0]
  1011. putserv "PRIVMSG $trivia_channel :Using\002 $stat_total_questions\002 questions in\002 $stat_total_cats\002 categories."
  1012. }
  1013. #>>>
  1014. # Merge two users
  1015. proc trivia_merge { nick param } {
  1016. #<<<
  1017. global trivia_db_handle
  1018. putloglev 4 * "trivia_merge ($nick, $param)"
  1019. if [regexp {^([^ ]+) (.+)$} $param matches nick1 nick2] {
  1020. set olduid [trivia_get_uid [mysqlescape $nick1]]
  1021. set newuid [trivia_get_uid [mysqlescape $nick2]]
  1022. if {$olduid == 0} {
  1023. puthelp "PRIVMSG $nick :Can't find user '$nick1'"
  1024. return 0
  1025. }
  1026. if {$newuid == 0} {
  1027. puthelp "PRIVMSG $nick :Can't find user '$nick2'"
  1028. return 0
  1029. }
  1030. puthelp "PRIVMSG $nick :Meging score for $nick1 into score for $nick2"
  1031. set sql "UPDATE scores SET user_id=$newuid WHERE user_id=$olduid"
  1032. mysqlexec $trivia_db_handle $sql
  1033. set newscore [trivia_get_score $newuid]
  1034. puthelp "PRIVMSG $nick :$nick2 now has $newscore points."
  1035. set sql "DELETE FROM users WHERE user_id = $olduid"
  1036. mysqlexec $trivia_db_handle $sql
  1037. puthelp "PRIVMSG $nick :User $nick1 has been deleted."
  1038. return 0
  1039. } else {
  1040. puthelp "PRIVMSG $nick :Use: !trivia merge <olduser> <newuser>"
  1041. puthelp "PRIVMSG $nick : olduser's score is merged with newuser's and olduser is deleted."
  1042. }
  1043. }
  1044. #>>>
  1045. # Find people who can start the script
  1046. # TODO: is this still used?
  1047. proc trivia_find_starters { } {
  1048. #<<<
  1049. global trivia_channel trivia_flag
  1050. set nicks [chanlist #triviacow &$trivia_flag]
  1051. if {[llength $nicks] > 0} {
  1052. return "One of the following users may be able to help: $nicks"
  1053. } else {
  1054. return "Sorry, noone who can start trivia is present."
  1055. }
  1056. }
  1057. #>>>
  1058. # Report a broken question
  1059. proc trivia_report { nick msg } {
  1060. #<<<
  1061. global trivia_channel trivia_db_handle trivia_last_qid trivia_c
  1062. if {$trivia_last_qid == 0} {
  1063. puthelp "PRIVMSG $trivia_channel :Sorry, unable to work out which question to report on :("
  1064. return 0
  1065. }
  1066. set nick [mysqlescape $nick]
  1067. set msg [mysqlescape $msg]
  1068. set sql "INSERT INTO reports VALUES (null, UNIX_TIMESTAMP(), '$nick', '$trivia_last_qid', '$msg', 'N')"
  1069. mysqlexec $trivia_db_handle $sql
  1070. 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)."
  1071. set trivia_last_qid 0
  1072. }
  1073. #>>>
  1074. ### WATCHDOG STUFF <<<
  1075. # Watchdog timer to make sure we're not ruined
  1076. proc trivia_watchdog { } {
  1077. #<<<
  1078. global trivia_last_ts trivia_watchdog_timer trivia_status trivia_channel
  1079. putloglev d * "trivia watchdog: tick"
  1080. if {$trivia_last_ts == 0} {
  1081. #never asked a question
  1082. set trivia_watchdog_timer [utimer 45 trivia_watchdog]
  1083. return 0
  1084. }
  1085. set current_ts [clock seconds]
  1086. set difference [expr $current_ts - $trivia_last_ts]
  1087. if {$difference > 60} {
  1088. if {$trivia_status == 1} {
  1089. putlog "watchdog: trivia is broken"
  1090. #putserv "PRIVMSG $trivia_channel :Oops, I think I'm broken. Attempting to recover..."
  1091. #set trivia_status 0
  1092. #trivia_start
  1093. putlog "trivia watchdog: current: $current_ts, last: $trivia_last_ts, difference: $difference"
  1094. }
  1095. }
  1096. set trivia_watchdog_timer [utimer 10 trivia_watchdog]
  1097. return 0
  1098. }
  1099. #>>>
  1100. # Kill the watchdog timeer
  1101. proc trivia_killwatchdog { } {
  1102. #<<<
  1103. global trivia_watchdog_timer
  1104. if {$trivia_watchdog_timer != ""} {
  1105. killutimer $trivia_watchdog_timer
  1106. }
  1107. }
  1108. #>>>
  1109. trivia_killwatchdog
  1110. set trivia_watchdog_timer [utimer 45 trivia_watchdog]
  1111. #>>>
  1112. putlog {TriviaEngine ENGAGED(*$£&($}
  1113. if {$trivia_must_rehash == 2} {
  1114. putlog "Auto-restarting trivia..."
  1115. trivia_start
  1116. set trivia_must_rehash 0
  1117. }