TriviaEngine-sqlite.tcl 44 KB

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