jillo-bot/src/commands/survey.ts

763 lines
24 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { SlashCommandBuilder } from '@discordjs/builders';
import { CommandInteraction, GuildMember, MessageActionRow, MessageButton, Client, Collection, MessageComponentInteraction, MessageSelectMenu, Modal, TextInputComponent, MessageSelectOptionData, MessageComponentType, TextChannel, TextInputStyleResolvable, Message } from 'discord.js';
import * as fs from 'fs/promises';
const SURVEY_CHANNEL = '983479509376434216';
const RESPONSES_CHANNEL = '983762973858361364';
const GENERAL_CHANNEL = '587108210683412493';
const ephemeral = true;
function extendOptions(opt: string[]): MessageSelectOptionData[] {
return opt.map(t => ({label: t, value: t.toLowerCase().replace(/[^a-z0-9-]/g, '-')}));
}
const survey = [
{},
{
text: 'What is your name?',
textResponse: true,
id: 'survey-name'
},/*
{
text: 'Your pronouns?',
components: [
new MessageSelectMenu()
.setCustomId('survey-pronouns')
.setOptions([
{
label: 'he/him',
value: 'he-him'
},
{
label: 'she/her',
value: 'she-her'
},
{
label: 'they/them',
value: 'they-them'
},
{
label: 'it/it',
value: 'it-it'
},
{
label: 'Other',
value: 'other',
description: 'You\'ll be able to specify later.'
}
])
.setMinValues(1)
.setMaxValues(5)
]
},
{
text: 'What are your interests/hobbies?',
textResponse: true,
id: 'survey-interests',
style: 'PARAGRAPH'
},
{
text: 'What do you see in this image?\nhttps://cdn.discordapp.com/attachments/789023763396165633/983471779060281364/unknown.png',
textResponse: true,
id: 'survey-image',
style: 'PARAGRAPH'
},
{
text: 'Which of the following game genres are you currently interested in?',
components: [
new MessageSelectMenu()
.setCustomId('survey-genre')
.setOptions([
{
label: 'FPS',
value: 'fps'
},
{
label: 'Strategy',
value: 'strategy'
},
{
label: 'Platformer',
value: 'platformer'
},
{
label: 'Rhythm Game',
value: 'rhythm-game'
},
{
label: 'Puzzle',
value: 'puzzle'
},
{
label: 'Shoot-em-up',
value: 'shmup'
}
])
.setMinValues(1)
.setMaxValues(6)
]
},
{
text: 'Favorite color?',
textResponse: true,
id: 'survey-color1',
placeholder: 'Red, orange, etc...'
},
{
text: 'Which of these music artists do you listen to?',
components: [
new MessageSelectMenu()
.setCustomId('survey-artist')
.setOptions([
{
label: 'Mr. Bill',
value: 'mr-bill'
},
{
label: 'Virtual Riot',
value: 'virtual-riot'
},
{
label: 'Mr. Beast',
value: 'mr-beast'
}
])
.setMinValues(1)
.setMaxValues(3)
]
},
{
text: 'What time is it currently?',
textResponse: true,
id: 'survey-time'
},
{
text: 'Did you provide the correct time?',
components: [
new MessageButton().setCustomId('survey-time-correct-yes').setLabel('Yes').setStyle('PRIMARY'),
new MessageButton().setCustomId('survey-time-correct-no').setLabel('No').setStyle('PRIMARY')
]
},
{
text: 'Favorite color?',
components: [
new MessageSelectMenu()
.setCustomId('survey-color2')
.setOptions([
{
label: 'Chair',
value: 'chair'
},
{
label: 'Lithium',
value: 'lithium'
},
{
label: 'Read a => String -> a',
value: 'read-signature'
},
{
label: 'Option 3',
value: 'option3'
}
])
.setMinValues(1)
.setMaxValues(4)
]
},
{
text: 'How many amperes have you had pushed into your heart at once in your lifetime?',
components: [
new MessageSelectMenu()
.setCustomId('survey-heart')
.setOptions([
{
label: '1',
value: 'one'
},
{
label: '2',
value: 'two'
},
{
label: '3',
value: 'three'
},
{
label: '4',
value: 'four'
},
{
label: '5',
value: 'five'
}
])
.setMinValues(1)
.setMaxValues(1)
]
},
{
text: 'Are you afraid of the dark?',
components: [
new MessageButton().setCustomId('survey-dark-yes').setLabel('Yes').setStyle('PRIMARY'),
new MessageButton().setCustomId('survey-dark-no').setLabel('No').setStyle('PRIMARY'),
new MessageButton().setCustomId('survey-dark-maybe').setLabel('Maybe').setStyle('PRIMARY')
]
},
{
text: 'Which of these statements on morality do you most resonate with?\nA. Morality is a science which can be understood. There are inherent moral truths in the world.\nB. There does not exist a moral reality, but only perceptions on what is good or bad.',
components: [
new MessageButton().setCustomId('survey-morality-a').setLabel('A').setStyle('PRIMARY'),
new MessageButton().setCustomId('survey-morality-b').setLabel('B').setStyle('PRIMARY')
]
},
{
text: 'How long ago did you last go on a bridge?',
components: [
new MessageSelectMenu()
.setCustomId('survey-bridge')
.setOptions(extendOptions(['A day ago', 'A week ago', 'A month ago', 'Several months ago', 'A year ago', 'Don\'t remember/Earlier']))
.setMinValues(1)
.setMaxValues(1)
]
},
{
text: 'Are you afraid of death?',
components: [
new MessageSelectMenu()
.setCustomId('survey-death')
.setOptions(extendOptions(['Yes', 'No', 'Maybe', 'Death is just a step to a different world']))
.setMinValues(1)
.setMaxValues(1)
]
},
{
text: 'What is your current heartrate?',
textResponse: true,
id: 'survey-heartrate',
placeholder: '90BPM, 120BPM, etc...'
},
{
text: 'You are operating a railroad track.\nA train is rapidly coming towards a track with 5 people strapped to the rails, unable to move. You are able to redirect the train onto another track, which has one person strapped to it. Do you make the switch and kill the one person, or do nothing and let the 5 people die?',
components: [
new MessageButton().setCustomId('survey-railroad1-switch').setLabel('Switch').setStyle('PRIMARY'),
new MessageButton().setCustomId('survey-railroad1-no').setLabel('Do not').setStyle('PRIMARY')
]
},
{
text: 'Which region do you live in?',
components: [
new MessageSelectMenu()
.setCustomId('survey-region')
.setOptions(extendOptions(['Europe', 'Asia', 'North America', 'South America', 'Moscow River', 'Africa', 'Oceania']))
.setMinValues(1)
.setMaxValues(1)
]
},
{
text: 'Select the character you resonate with the most.',
components: [
new MessageSelectMenu()
.setCustomId('survey-character')
.setOptions([
{
label: '⯧',
value: 'a'
},
{
label: '▩',
value: 'b'
},
{
label: '',
value: 'c'
},
{
label: '∵',
value: 'd'
},
{
label: '⨆',
value: 'e'
}])
.setMinValues(1)
.setMaxValues(1)
]
},
{
text: 'Which of these statements describe you the best?',
components: [
new MessageSelectMenu()
.setCustomId('survey-statement')
.setOptions(extendOptions(['Is that a cut on your face, or part of your eye?', 'The gash weaves down as if you cry', 'The pain itself is reason why']))
.setMinValues(1)
.setMaxValues(1)
]
},
{
text: 'When was the last time you experienced a power surge?',
components: [
new MessageSelectMenu()
.setCustomId('survey-power-surge')
.setOptions(extendOptions(['A day ago', 'A week ago', 'A month ago', 'Several months ago', 'A year ago', 'Don\'t remember/Earlier']))
.setMinValues(1)
.setMaxValues(1)
]
},
{
text: 'Have you ever shown any interest in automobiles and cars?',
components: [
new MessageButton().setCustomId('survey-cars1-yes').setLabel('Yes').setStyle('PRIMARY'),
new MessageButton().setCustomId('survey-cars1-no').setLabel('No').setStyle('PRIMARY')
]
},
{
text: 'You are operating a railroad track.\nA person is strapped to the rails unable to move, however there is a fork right before that splits off to a different track with no people. Do you switch to save the person?',
components: [
new MessageSelectMenu()
.setCustomId('survey-railroad2')
.setOptions([
{
label: 'Yes',
value: 'yes'
},
{
label: 'Bloodshed',
value: 'bloodshed',
description: 'The correct choice'
}
])
.setMinValues(1)
.setMaxValues(1)
]
},
{
text: 'Have you been here before?',
components: [
new MessageButton().setCustomId('survey-been-here-yes').setLabel('Yes').setStyle('PRIMARY'),
new MessageButton().setCustomId('survey-been-here-no').setLabel('No').setStyle('PRIMARY')
]
},*/
{
text: 'Please provide your social media accounts.',
textResponse: true,
id: 'survey-socialmedia',
style: 'PARAGRAPH',
placeholder: 'Twitter, YouTube, etc. Please provide as many as possible.'
},/*
{
text: 'Is your refrigerator running?',
components: [
new MessageButton().setCustomId('survey-fridge-yes').setLabel('Yes').setStyle('PRIMARY'),
new MessageButton().setCustomId('survey-fridge-no').setLabel('No').setStyle('PRIMARY')
]
},
{
text: 'Do you generally feel unsafe in your life?',
noNumber: true,
components: [
new MessageSelectMenu()
.setCustomId('survey-unsafe')
.setOptions(extendOptions(['Yes', 'No', 'Sometimes']))
.setMinValues(1)
.setMaxValues(1)
]
},
{
text: 'train',
components: [
new MessageSelectMenu()
.setCustomId('survey-train')
.setOptions(extendOptions(['Option 1', 'Option 2']))
.setMinValues(1)
.setMaxValues(1)
]
},
{
text: 'Do you live on planet Earth?',
components: [
new MessageButton().setCustomId('survey-earth-yes').setLabel('Yes').setStyle('PRIMARY'),
new MessageButton().setCustomId('survey-earth-no').setLabel('No').setStyle('PRIMARY'),
new MessageButton().setCustomId('survey-earth-maybe').setLabel('Maybe~ ;3').setStyle('PRIMARY')
]
},
{
text: 'Do you own a credit or debit card?',
components: [
new MessageButton().setCustomId('survey-credit-card-yes').setLabel('Yes').setStyle('PRIMARY'),
new MessageButton().setCustomId('survey-credit-card-no').setLabel('No').setStyle('PRIMARY')
]
},
{
text: 'You are operating a railroad track.\nYou will be alone on your shift this night, with no one to watch, leaving you with a perfect opportunity to strap an explosive on one of the tracks. Do you cause a horrible accident resulting in the deaths of thousands?',
components: [
new MessageButton().setCustomId('survey-railroad3-yes').setLabel('Yes').setStyle('PRIMARY'),
new MessageButton().setCustomId('survey-railroad3-option-2').setLabel('Option 2').setStyle('PRIMARY')
]
},
{
text: 'Please click on the button when you\'re finished watching the video.\nhttps://www.youtube.com/watch?v=FsTCet79i2k',
components: [
new MessageButton().setCustomId('survey-finished').setLabel('Done').setStyle('PRIMARY')
]
},
{
text: 'Which of these natural landmarks is your favorite?',
components: [
new MessageSelectMenu()
.setCustomId('survey-landmark')
.setOptions(extendOptions(['Darvaza Gas Crater', 'Nazca Lines', 'Bermuda Triangle', 'The Hole']))
.setMinValues(1)
.setMaxValues(1)
]
},
{
text: 'Have you ever shown any interest in automobiles and cars?',
components: [
new MessageButton().setCustomId('survey-cars2-yes').setLabel('Yes').setStyle('PRIMARY'),
new MessageButton().setCustomId('survey-cars2-no').setLabel('No').setStyle('PRIMARY')
]
},
{
text: 'Are you operating a railroad track?',
components: [
new MessageButton().setCustomId('survey-railroad4-yes').setLabel('Yes').setStyle('PRIMARY'),
new MessageButton().setCustomId('survey-railroad4-no').setLabel('No').setStyle('PRIMARY')
]
},
{
text: 'Please explain why/why not.',
textResponse: true,
id: 'survey-railroad4-why',
style: 'PARAGRAPH'
},
{
text: 'Were you telling the truth in your previous 2 anwsers?',
components: [
new MessageButton().setCustomId('survey-railroad4-truth-yes').setLabel('Yes').setStyle('PRIMARY'),
new MessageButton().setCustomId('survey-railroad4-truth-no').setLabel('No').setStyle('PRIMARY')
]
},*/
{
text: 'Please rate your experience with this survey.',
noNumber: true,
components: [
new MessageSelectMenu()
.setCustomId('survey-rating')
.setOptions(extendOptions(['1 ⭐', '2 ⭐⭐', '3 ⭐⭐⭐', '4 ⭐⭐⭐⭐', '5 ⭐⭐⭐⭐⭐']))
.setMinValues(1)
.setMaxValues(1)
]
},/*
{
text: 'What is your opinion on mycology?',
noNumber: true,
textResponse: true,
id: 'survey-mycology',
style: 'PARAGRAPH'
},
{
text: 'Do you like mushrooms as a concept?',
noNumber: true,
components: [
new MessageButton().setCustomId('survey-closer6').setLabel('Yes.').setStyle('SUCCESS')
]
},
{
text: 'Do you like how mushrooms grow and expand?',
noNumber: true,
components: [
new MessageButton().setCustomId('survey-closer7').setLabel('Yes.').setStyle('SUCCESS')
]
},*/
{
text: 'Would you become a stepping stone for mushrooms to grow and expand?',
noNumber: true,
components: [
new MessageButton().setCustomId('survey-closer8').setLabel('YES YES YES YES YES YES YES YES YES YES YES YES YES YES YES YES YES YES YES YES').setStyle('SUCCESS')
]
}
];
interface SurveyResponse {
id: string,
type: MessageComponentType,
value: string | string[] | null,
values: string[] | null,
questionIndex: number,
questionText: string,
questionOptions: string[] | null,
}
const surveyProgress: Collection<string, number> = new Collection();
const surveyInteractions: Collection<string, MessageComponentInteraction> = new Collection();
const surveyAnwsered: Collection<string, Collection<string, boolean>> = new Collection();
const surveyAnwsers: Collection<string, Partial<SurveyResponse>[]> = new Collection();
const surveyCurrentMessage: Collection<string, string> = new Collection();
function pushResponse(resp: Partial<SurveyResponse>, userId: string) {
const i = surveyProgress.get(userId);
const question = survey[i || 0];
if (question) {
resp.questionIndex = i;
resp.questionText = question.text;
if (resp.values && question.components) {
const components = question.components as MessageSelectMenu[];
resp.questionOptions = components[0].options.map(o => o.label);
resp.values = resp.values.map(v => components[0].options.find(opt => opt.value === v)?.label || '');
if (components[0].maxValues === 1) {
resp.value = resp.values[0];
resp.values = undefined;
}
}
if (resp.type === 'BUTTON' && question.components) {
const components = question.components as MessageButton[];
resp.value = components.find(opt => opt.customId === resp.id)?.label;
resp.questionOptions = components.map(o => o?.label!);
}
}
// for ordering purposes
const res = {
id: resp.id,
type: resp.type,
questionIndex: resp.questionIndex,
questionText: resp.questionText,
questionOptions: resp.questionOptions || null,
value: resp.value || resp.values || null
};
if (!surveyAnwsers.get(userId)) {
surveyAnwsers.set(userId, []);
}
if (res.questionIndex) {
surveyAnwsers.get(userId)!.push(res);
}
}
function resetProgress(userId: string) {
surveyAnwsered.set(userId, new Collection());
surveyProgress.set(userId, 0);
surveyAnwsers.set(userId, []);
surveyCurrentMessage.delete(userId);
}
async function advanceSurvey(userId: string, dontAdvanceProgress = false) {
if (!dontAdvanceProgress) surveyProgress.set(userId, (surveyProgress.get(userId) || 0) + 1);
const interaction = surveyInteractions.get(userId)!;
const i = surveyProgress.get(userId)!;
const question = survey[i];
if (!question) {
const anwsers = surveyAnwsers.get(userId);
const filename = `survey-responses/survey-response-${userId}.json`;
const stringified = JSON.stringify(anwsers, undefined, 2);
await fs.writeFile(filename, stringified, {encoding: 'utf8'});
(await interaction.client.channels.fetch(RESPONSES_CHANNEL) as TextChannel).send({
content: `Recieved a response from <@${userId}>`,
files: [filename],
components: [new MessageActionRow().addComponents(
new MessageButton().setLabel('Approve').setStyle('SUCCESS').setCustomId(`survey-reply-approve-${userId}`),
new MessageButton().setLabel('Deny').setStyle('DANGER').setCustomId(`survey-reply-deny-${userId}`))
]
});
await interaction.deferReply({
ephemeral: ephemeral
});
await interaction.followUp({
content: '**Thank you for your participation!** Your responses have been recorded and you will soon become a member of the Fire Pit based on your anwsers.',
ephemeral: ephemeral
});
resetProgress(userId);
} else {
let components: (MessageSelectMenu | MessageButton)[] = [];
if (question.components) components = question.components;
if (question.textResponse) components.push(
new MessageButton().setCustomId(question.id + '-modal').setLabel('Anwser').setStyle('PRIMARY')
);
const msg = await interaction.reply({
content: `${question.noNumber ? '' : `${i}. `}${question.text!.split('\n')[0] === '' ? '' : `**${question.text!.split('\n')[0]}**`}\n${question.text!.split('\n').slice(1).join('\n')}`,
components: components ? ([new MessageActionRow().addComponents(...components)]) : undefined,
ephemeral: ephemeral,
fetchReply: true
});
surveyCurrentMessage.set(userId, msg.id);
}
}
module.exports = {
data: new SlashCommandBuilder()
.setName('createsurvey')
.setDescription('Re-create the survey button'),
execute: async (interaction: CommandInteraction) => {
const row = new MessageActionRow().addComponents(
new MessageButton().setCustomId('survey-take').setLabel('Take Survey').setStyle('SECONDARY')
);
await interaction.channel!.send({
content: '**Hello!**\n\nIt would be great to know more about you and your interests before you\'re accepted into the Discord server, so please answer some simple questions for us!',
components: [row]
});
await interaction.reply({
content: 'done',
ephemeral: true
});
},
onClientReady: (bot: Client) => {
bot.on('interactionCreate', async (interaction) => {
if (!interaction.isMessageComponent()) return;
if (interaction.isModalSubmit()) return;
if (!interaction.customId.startsWith('survey-')) return;
if (!interaction.member) return;
const member = interaction.member as GuildMember;
if (interaction.customId.startsWith('survey-reply-approve-') || interaction.customId.startsWith('survey-reply-deny-')) {
const approve = interaction.customId.startsWith('survey-reply-approve-');
const id = interaction.customId.split('-')[3];
if (!approve) {
(await interaction.guild?.members.fetch(id))!.kick();
await interaction.reply({
content: `${member} denied the application.`,
ephemeral: false
});
const msg = interaction.message as Message;
await msg.edit({
content: msg.content,
components: [new MessageActionRow().addComponents(
new MessageButton().setCustomId('survey-reply-denied').setLabel(`${member.displayName} denied the application`).setStyle('DANGER').setDisabled(true)
)]
});
} else {
(await interaction.guild?.members.fetch(id))!.roles.add(member.guild!.roles.cache.find(r => r.name === '✦')!);
await interaction.reply({
content: `${member} approved the application.`,
ephemeral: false
});
const msg = interaction.message as Message;
await msg.edit({
content: msg.content,
components: [new MessageActionRow().addComponents(
new MessageButton().setCustomId('survey-reply-approved').setLabel(`${member.displayName} approved the application`).setStyle('SUCCESS').setDisabled(true)
)]
});
(await interaction.guild?.channels.fetch(GENERAL_CHANNEL) as TextChannel).send({
content: `_<@${id}> has been approved into the server._`,
});
}
return;
}
surveyInteractions.set(member.id, interaction);
if (interaction.customId === 'survey-take') {
const index = surveyProgress.get(member.id);
if (index && index > 0) {
const currentMessage = surveyCurrentMessage.get(member.id);
if (currentMessage) {
surveyAnwsered.get(member.id)!.set(currentMessage, true);
}
advanceSurvey(member.id, true);
return;
} else {
resetProgress(member.id);
}
} else {
if (!surveyAnwsered.get(member.id)) {
interaction.deferUpdate();
return;
}
}
if (surveyAnwsered.get(member.id)!.get(interaction.message.id)) {
interaction.deferUpdate();
return;
}
if (interaction.customId.endsWith('-modal')) {
const modal = new Modal()
.setCustomId(interaction.customId)
.setTitle('Fire Pit Survey');
const i = surveyProgress.get(member.id)!;
const question = survey[i];
const input = new TextInputComponent()
.setCustomId(question.id!)
.setLabel(question.text!.trim().split('\n')[0].slice(0, 44))
.setStyle((question.style as TextInputStyleResolvable) || 'SHORT')
.setPlaceholder(question.placeholder || '')
.setRequired(true);
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const row = new MessageActionRow().addComponents(input);
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
modal.addComponents(row);
interaction.showModal(modal);
} else {
surveyAnwsered.get(member.id)!.set(interaction.message.id, true);
const resp = {
id: interaction.customId,
type: interaction.componentType,
values: interaction.isSelectMenu() ? interaction.values : undefined
};
pushResponse(resp, member.id);
advanceSurvey(member.id);
}
});
bot.on('interactionCreate', interaction => {
if (!interaction.isModalSubmit()) return;
if (!interaction.member) return;
const member = interaction.member as GuildMember;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
surveyInteractions.set(member.id, interaction);
surveyAnwsered.get(member.id)!.set(interaction.message!.id, true);
const i = surveyProgress.get(member.id)!;
const question = survey[i];
const field = interaction.fields.getField(question.id!);
const resp = {
id: field.customId,
type: field.type,
value: field.value
};
pushResponse(resp, member.id);
advanceSurvey(member.id);
});
bot.on('messageCreate', msg => {
if (msg.channel.id !== SURVEY_CHANNEL) return;
if (msg.author.id === msg.client.user!.id) return;
msg.delete();
});
}
};