Skip to content

Instantly share code, notes, and snippets.

@rawnly
Created January 18, 2021 20:29
Show Gist options
  • Save rawnly/a3b3ca86d4e0cd9ccd723ed6715c8cd2 to your computer and use it in GitHub Desktop.
Save rawnly/a3b3ca86d4e0cd9ccd723ed6715c8cd2 to your computer and use it in GitHub Desktop.
import ms from 'ms'
import { DMChannel, Message, NewsChannel, TextChannel } from 'discord.js'
import { REACTIONS } from './reactions';
type Channel = TextChannel | DMChannel | NewsChannel
type QuestionType = 'confirm' | 'text';
export type Question = {
name: string;
message: String;
type: QuestionType;
timeout?: number;
}
type AnsweredQuestion = Question & {
answer: boolean | string;
}
const isMessage = (m: any): m is Message => !!m.content
export class Prompt {
private channel: Channel = null
readonly questions : Question[] = []
addQuestion(question: Question): Prompt {
this.questions.push(question)
return this
}
setChannel(messageOrChannel: Channel | Message): Prompt {
if ( isMessage(messageOrChannel) ) {
this.channel = messageOrChannel.channel
return this
}
this.channel = messageOrChannel
return this
}
private async _askQuestion(question: Question, channel: Channel): Promise<AnsweredQuestion> {
const msg = await channel.send(`${question.message}`)
let answer: string | boolean = null
switch ( question.type ) {
case 'text': {
const collected = await msg.channel.awaitMessages(m => true, {
max: 1,
time: question.timeout ?? ms('10s'),
errors: ['time']
})
answer = collected.first().content
break
}
case 'confirm': {
await Promise.all([
msg.react(REACTIONS.GREEN_CHECKMARK),
msg.react(REACTIONS.TIMES)
])
const collected = await msg.awaitReactions(r => [REACTIONS.GREEN_CHECKMARK, REACTIONS.TIMES].includes(r.emoji.name), {
max: 1,
time: question.timeout ?? ms('10s'),
errors: ['time']
})
answer = collected.first().emoji.name === REACTIONS.GREEN_CHECKMARK
break
}
}
return {
...question,
answer
};
}
// Promise<Record<string, string | boolean>>
private async* _askTo(messageOrChannel?: Message | Channel): AsyncGenerator<AnsweredQuestion> {
let channel = this.channel;
if ( !!messageOrChannel ) {
if ( isMessage(messageOrChannel) ) {
channel = messageOrChannel.channel
} else {
channel = messageOrChannel
}
}
if ( !channel ) {
throw new SyntaxError(`Cannot obtain channel. Channel is "${channel}"`)
}
for (let i=0; i<this.questions.length; i++) {
const question = this.questions[i];
yield this._askQuestion(question, channel)
}
}
async collect(messageOrChannel?: Message | Channel): Promise<Record<string, string|boolean>> {
return Prompt.mapResultToObject(this._askTo(messageOrChannel))
}
static async mapResultToObject(iterator: AsyncGenerator<AnsweredQuestion>): Promise<Record<string, string|boolean>> {
const out: Record<string, string|boolean> = {}
for await ( const item of iterator ) {
out[item.name] = item.answer
}
return out
}
}
// inside some handler
const answers = await new Prompt()
.setChannel(msg.channel) // msg = Message
.addQuestion({
name: 'first',
message: 'What is your name?',
type: 'text',
})
.addQuestion({
name: 'last_name',
message: 'What is your last name?',
type: 'text',
})
.addQuestion({
name: 'confirmed',
message: 'Are you sure about your questions?',
type: 'confirm'
})
.collect()
console.table(answers)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment