TriviaEngine-sqlite.tcl 48 KB

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