Skip to content

Commit 7962f7f

Browse files
committed
[backport] enhance /trivia UX and fix /trivia list
1 parent aceb427 commit 7962f7f

1 file changed

Lines changed: 54 additions & 52 deletions

File tree

server/bot/commands/trivia.ts

Lines changed: 54 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Command, DB } from '../imports/types'
33
import { getInsult } from '../imports/tools'
44
import fs from 'fs'
55

6-
async function parseTriviaList (fileName: string) {
6+
async function parseTriviaList (fileName: string): Promise<Map<string, string[]>> {
77
const data = await fs.promises.readFile(`./triviaLists/${fileName}.txt`, 'utf8')
88
const triviaList = new Map<string, string[]>()
99
data.split('\n').forEach(el => {
@@ -22,7 +22,7 @@ export class TriviaSession {
2222
questionList: Map<string, string[]>
2323
scores: { [id: string]: number } = {}
2424
stopped = false
25-
timer: number = null
25+
timer: number | null = null
2626
timeout = Date.now()
2727
count = 0
2828
tempDB: DB
@@ -38,25 +38,29 @@ export class TriviaSession {
3838
this.client = client
3939
}
4040

41-
getScores (addMedals = false) {
41+
getScores (addMedals = false): { embed: EmbedOptions } {
4242
const currentScores = Object.entries(this.scores).sort(([, a], [, b]) => b - a)
4343
const medals: { [id: string]: string } = {}
4444
if (addMedals) {
45-
const maxReduce = (a: number, b: number) => Math.max(a || 0, b || 0)
45+
const maxReduce = (a: number, b: number): number => Math.max(a || 0, b || 0)
4646
const values = Object.values(this.scores)
4747
const first = values.reduce(maxReduce)
4848
const second = values.filter(num => num !== first).reduce(maxReduce, 0)
4949
const third = values.filter(num => num !== first && num !== second).reduce(maxReduce, 0)
5050
currentScores.forEach(([id, b]) => {
51-
medals[id] = first && b === first ? '🥇 '
52-
: second && b === second ? '🥈 '
51+
medals[id] = first && b === first
52+
? '🥇 '
53+
: second && b === second
54+
? '🥈 '
5355
: third && b === third ? '🥉 ' : ''
5456
})
5557
}
5658
const member = this.message.member.guild.members.get(this.client.user.id)
57-
const color = member ? (member.roles.map(i => member.guild.roles.get(i)).sort(
58-
(a, b) => a.position > b.position ? -1 : 1
59-
).find(i => i.color !== 0) || { color: 0 }).color : 0
59+
const color = member
60+
? (member.roles.map(i => member.guild.roles.get(i)).sort(
61+
(a, b) => a.position > b.position ? -1 : 1
62+
).find(i => i.color !== 0) || { color: 0 }).color
63+
: 0
6064
const embed: EmbedOptions = {
6165
title: 'Scores',
6266
color,
@@ -70,14 +74,14 @@ export class TriviaSession {
7074
return { embed }
7175
}
7276

73-
async endGame () {
77+
async endGame (): Promise<void> {
7478
this.stopped = true
7579
delete this.tempDB.trivia[this.channel.id]
7680
if (Object.keys(this.scores).length > 0) await this.channel.createMessage(this.getScores(true))
7781
}
7882

79-
async newQuestion () {
80-
for (let i of Object.values(this.scores)) {
83+
async newQuestion (): Promise<boolean> {
84+
for (const i of Object.values(this.scores)) {
8185
if (i === this.settings.maxScore) {
8286
await this.endGame()
8387
return true
@@ -107,7 +111,7 @@ export class TriviaSession {
107111

108112
if (this.currentQuestion === null) {
109113
await new Promise(resolve => setTimeout(resolve, 1000))
110-
if (this.stopped === false) await this.newQuestion()
114+
if (!this.stopped) await this.newQuestion()
111115
} else if (this.stopped) {
112116
return true
113117
} else {
@@ -129,11 +133,11 @@ export class TriviaSession {
129133
await this.channel.createMessage(message)
130134
await this.channel.sendTyping()
131135
await new Promise(resolve => setTimeout(resolve, 1000))
132-
if (this.stopped === false) await this.newQuestion()
136+
if (!this.stopped) await this.newQuestion()
133137
}
134138
}
135139

136-
async checkAnswer (message: Message) {
140+
async checkAnswer (message: Message): Promise<boolean> {
137141
if (message.author.bot || this.currentQuestion === null) {
138142
return false
139143
}
@@ -142,8 +146,8 @@ export class TriviaSession {
142146

143147
// TODO: fuse.js for better pattern matching.
144148
for (let i = 0; i < this.currentQuestion[1].length; i++) {
145-
let answer = this.currentQuestion[1][i].toLowerCase()
146-
let guess = message.content.toLowerCase()
149+
const answer = this.currentQuestion[1][i].toLowerCase()
150+
const guess = message.content.toLowerCase()
147151
if (!answer.includes(' ')) { // Strict answer checking for one word answers
148152
const guessWords = guess.split(' ')
149153
for (let j = 0; j < guessWords.length; j++) {
@@ -173,49 +177,47 @@ export const handleTrivia: Command = {
173177
opts: {
174178
description: 'Start a trivia game on a topic of your choice.',
175179
fullDescription: `Start a trivia game on a topic of your choice.
176-
**Default settings:** IveBot gains points: no, seconds to answer: 15, points needed to win: 30, reveal answer on timeout: yes`,
177-
usage: `/trivia <topic> (--bot-plays=true|false) (--time-limit=<time longer than 4s>) (--max-score=<points greater than 0>) (--reveal-answer=true|false)
178-
**During a trivia game:** /trivia (scoreboard/score/scores/stop)
180+
**Default settings:** IveBot plays: no, seconds to answer: 15, points to win: 30, reveal answer: yes`,
181+
usage: `/trivia <topic> (botplays) (revealanswer) (timelimit=<5 seconds or more>) (pointstowin=<1 point or more>)
182+
**During a trivia game:** /trivia (scoreboard/score/scores/stop/end)
179183
**To view available topics:** /trivia list`,
180-
example: '/trivia greekmyth --bot-plays=true',
184+
example: '/trivia greekmyth botplays',
181185
guildOnly: true,
182186
argsRequired: true
183187
},
184-
generator: async (message: Message, args: string[], { tempDB, client }) => {
188+
generator: async (message, args, { tempDB, client }) => {
185189
let botPlays = false
186190
let timeLimit = 15000
187191
let maxScore = 30
188192
let revealAnswer = true
189193

190-
// TODO: Flag soup can be handled better.
191-
if (args.find(element => element.includes('--bot-plays='))) {
192-
if (args.find(element => element.includes('--bot-plays=')).split('=')[1] === 'true') {
194+
const botPlaysFlag = args.find(element => element.startsWith('botplays=') || element === 'botplays')
195+
if (botPlaysFlag) {
196+
if (botPlaysFlag.split('=')[1] === 'true' || botPlaysFlag === 'botplays') {
193197
botPlays = true
194-
} else if (args.find(element => element === '--bot-plays=').split('=')[1] !== 'false') {
195-
return 'Invalid usage. It must be either true or false.'
198+
} else if (botPlaysFlag.split('=')[1] !== 'false') {
199+
return 'Invalid usage. `botplays` must be either true or false.'
196200
}
197201
}
198-
if (args.find(element => element.includes('--time-limit='))) {
199-
if (+args.find(element => element.includes('--time-limit=')).split('=')[1] > 4) {
200-
timeLimit = +args.find(element => element.includes('--time-limit=')).split('=')[1] * 1000
201-
} else {
202-
return 'Invalid usage. It must be a number greater than 4.'
203-
}
202+
const timeLimitFlag = args.find(element => element.startsWith('timelimit='))?.split('=')
203+
if (timeLimitFlag) {
204+
if (!isNaN(+timeLimitFlag[1]) && +timeLimitFlag[1] > 4) timeLimit = +timeLimitFlag[1] * 1000
205+
else return 'Invalid usage. `timelimit` must be a number greater than 4.'
204206
}
205-
if (args.find(element => element.includes('--max-score='))) {
206-
if (+args.find(element => element.includes('--max-score=')).split('=')[1] > 0) {
207-
maxScore = +args.find(element => element.includes('--max-score')).split('=')[1]
208-
} else {
209-
return 'Invalid usage. It must be a number greater than 0.'
210-
}
207+
const maxScoreFlag = args.find(element => element.startsWith('pointstowin='))?.split('=')
208+
if (maxScoreFlag) {
209+
if (+maxScoreFlag[1] > 0) maxScore = +maxScoreFlag[1]
210+
else return 'Invalid usage. `pointstowin` must be a number greater than 0.'
211211
}
212-
if (args.find(element => element.includes('--reveal-answer='))) {
213-
if (args.find(element => element.includes('--reveal-answer=')).split('=')[1] === 'false') {
212+
const revealAnswerFlag = args.find(element => element.startsWith('revealanswer=') || element === 'revealanswer')
213+
if (revealAnswerFlag) {
214+
if (revealAnswerFlag.split('=')[1] === 'false') {
214215
revealAnswer = false
215-
} else if (args.find(element => element === '--reveal-answer=').split('=')[1] !== 'true') {
216-
return 'Invalid usage. It must be either true or false.'
216+
} else if (revealAnswerFlag.includes('=') && revealAnswerFlag.split('=')[1] !== 'true') {
217+
return 'Invalid usage. `revealanswer` must be either true or false.'
217218
}
218219
}
220+
219221
if (args.length === 1 && ['scores', 'score', 'leaderboard'].includes(args[0])) {
220222
const session = tempDB.trivia[message.channel.id]
221223
if (session) {
@@ -226,16 +228,16 @@ export const handleTrivia: Command = {
226228
} else if (args.length === 1 && args[0] === 'list') {
227229
const lists = await fs.promises.readdir('./triviaLists/')
228230
const member = message.member.guild.members.get(client.user.id)
229-
const color = member ? (member.roles.map(i => member.guild.roles.get(i)).sort(
230-
(a, b) => a.position > b.position ? -1 : 1
231-
).find(i => i.color !== 0) || { color: 0 }).color : 0
232-
const embed: EmbedOptions = {
233-
title: 'Available trivia lists',
234-
color,
235-
fields: lists.map(name => ({ name: name.replace('.txt', ''), value: '_ _', inline: true }))
231+
const color = member
232+
? (member.roles.map(i => member.guild.roles.get(i)).sort(
233+
(a, b) => a.position > b.position ? -1 : 1
234+
).find(i => i.color !== 0) || { color: 0 }).color
235+
: 0
236+
return {
237+
content: '❔ **Available trivia topics:**',
238+
embed: { color, description: lists.map(name => `**${name.replace('.txt', '')}**`).join(', ') }
236239
}
237-
return { embed }
238-
} else if (args.length === 1 && args[0] === 'stop') {
240+
} else if (args.length === 1 && (args[0] === 'stop' || args[0] === 'end')) {
239241
const session = tempDB.trivia[message.channel.id]
240242
const channel = message.channel as GuildTextableChannel
241243
if (session) {

0 commit comments

Comments
 (0)