TriviaEngine-sqlite.tcl 30 KB

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