434 lines
11 KiB
JavaScript
434 lines
11 KiB
JavaScript
import fs from 'fs';
|
|
import Discord from 'discord.js';
|
|
|
|
import { botToken, discordToken } from './config.json';
|
|
|
|
const DISCORD_TOKEN = discordToken;
|
|
const BOT_TOKEN = botToken;
|
|
const BOT_DISCORD_CHANNEL = '463832055944445962';
|
|
|
|
const servers = [
|
|
{
|
|
name: 'Trivia Community',
|
|
id: '414461171974930434',
|
|
channels: [
|
|
{
|
|
name: 'public-hq',
|
|
id: '449575067333033984',
|
|
weightModifier: 0.3,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
name: 'Trivia Party',
|
|
id: '460145544225095680',
|
|
channels: [
|
|
{
|
|
name: 'hq',
|
|
id: '460146376358101003',
|
|
weightModifier: 0.2,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
name: 'HUB',
|
|
id: '452222497312604170',
|
|
channels: [
|
|
{
|
|
name: 'hq-trivia',
|
|
id: '452223090508693515',
|
|
weightModifier: 0.8,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
name: 'Trivia Addicts',
|
|
id: '447177515522588672',
|
|
channels: [
|
|
{
|
|
name: 'hq',
|
|
id: '450174797780090901',
|
|
weightModifier: 0.7,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
name: 'Trivia Nation',
|
|
id: '436303863885070357',
|
|
channels: [
|
|
{
|
|
name: 'quizzee-boi',
|
|
id: '440663723770773506',
|
|
weightModifier: 0.15,
|
|
},
|
|
],
|
|
}
|
|
|
|
];
|
|
|
|
const weights = {
|
|
'standard': 10,
|
|
'apg': 5,
|
|
'not': -10,
|
|
'w': 1,
|
|
'?': 3,
|
|
'apb': 1,
|
|
'its': 15,
|
|
};
|
|
|
|
export default class Scraper {
|
|
constructor() {
|
|
this.answers = [];
|
|
this.questionId = null;
|
|
this.collect = false;
|
|
this.weights = [0, 0, 0];
|
|
this.botClient = null;
|
|
this.timer = null;
|
|
this.players = {};
|
|
this.lastWeights = null;
|
|
this.ticker = 3;
|
|
}
|
|
|
|
run() {
|
|
const client = new Discord.Client();
|
|
|
|
client.on('ready', () => {
|
|
console.log(`Discord Scraper... Logged in as ${client.user.tag}!`);
|
|
});
|
|
|
|
client.on('message', msg => {
|
|
if (this.collect) {
|
|
this.scrapeContents(msg);
|
|
}
|
|
});
|
|
|
|
client.login(DISCORD_TOKEN);
|
|
|
|
if (BOT_TOKEN) {
|
|
this.botClient = new Discord.Client();
|
|
|
|
this.botClient.on('ready', () => {
|
|
console.log(`Discord Scraper... Bot logged in!`);
|
|
});
|
|
|
|
this.botClient.login(BOT_TOKEN);
|
|
}
|
|
}
|
|
|
|
getWeights() {
|
|
return this.weights;
|
|
}
|
|
|
|
endQuestion(questionSummary, answerIndex) {
|
|
this.scoreUsers(answerIndex);
|
|
this.saveContents();
|
|
this.collect = false;
|
|
if (this.timer) clearInterval(this.timer);
|
|
this.showSummary(questionSummary, answerIndex);
|
|
}
|
|
|
|
startQuestion(question) {
|
|
const { questionId } = question;
|
|
|
|
this.collect = true;
|
|
this.questionId = questionId;
|
|
this.answers = [];
|
|
this.weights = [0, 0, 0];
|
|
this.lastWeights = null;
|
|
this.ticker = 3;
|
|
const showUpdate = () => this.updateAnswers(question);
|
|
const delayUpdate = () => this.timer = setInterval(showUpdate.bind(this), 1000);
|
|
setTimeout(delayUpdate.bind(this), 3000);
|
|
this.showQuestion(question);
|
|
}
|
|
|
|
updateAnswers(question) {
|
|
const { answers } = question;
|
|
|
|
if (this.botClient) {
|
|
const channel = this.botClient.channels.get(BOT_DISCORD_CHANNEL);
|
|
const normalizedWeights = this.normalizeWeights();
|
|
if (channel) {
|
|
channel.send(`**Normalized Weights**\`\`\`[${normalizedWeights[0]}%, ${normalizedWeights[1]}%, ${normalizedWeights[2]}%]\t\t${this.getSuggestion()}\`\`\``);
|
|
// channel.send(`**Weight Deltas**\`\`\`[${differenceInWeights[0]}, ${differenceInWeights[1]}, ${differenceInWeights[2]}]\`\`\``);
|
|
}
|
|
}
|
|
|
|
this.ticker++;
|
|
}
|
|
|
|
getSuggestion() {
|
|
const normalizedWeights = this.normalizeWeights();
|
|
let suggestion;
|
|
|
|
const majority = normalizedWeights.find(weight => weight > 66);
|
|
if (majority) {
|
|
switch (normalizedWeights.indexOf(majority)) {
|
|
case 0: suggestion = 'Choose 1'; break;
|
|
case 1: suggestion = 'Choose 2'; break;
|
|
case 2: suggestion = 'Choose 3'; break;
|
|
}
|
|
|
|
return suggestion;
|
|
}
|
|
|
|
let splitChoices = normalizedWeights.filter(weight => weight >= 20);
|
|
if (splitChoices.length === 3 || splitChoices.length === 0) return 'Split 1 2 3';
|
|
|
|
splitChoices = normalizedWeights.map((weight, index) => {
|
|
if (weight >= 20) return index;
|
|
return null;
|
|
});
|
|
|
|
const nullChoices = splitChoices.filter(choice => choice === null);
|
|
if (nullChoices.length < 2) {
|
|
suggestion = 'Split';
|
|
} else {
|
|
suggestion = 'Choose';
|
|
}
|
|
|
|
splitChoices.forEach(choiceIndex => {
|
|
if (choiceIndex !== null) {
|
|
suggestion += ` ${choiceIndex + 1}`;
|
|
}
|
|
});
|
|
|
|
return suggestion;
|
|
}
|
|
|
|
normalizeWeights(numberOfPlayers = 100) {
|
|
let nonZeroWeights = this.weights.filter(weight => weight >= 0);
|
|
if (nonZeroWeights.length === 0) {
|
|
nonZeroWeights = [0, 0, 0];
|
|
}
|
|
let totalWeight = nonZeroWeights
|
|
.reduce((accumulator, currentValue) => accumulator + currentValue);
|
|
|
|
let normalized = [];
|
|
|
|
normalized = this.weights.map(weight => {
|
|
if (totalWeight === 0) return Math.round((1 / 3) * numberOfPlayers);
|
|
|
|
const zeroNegatives = weight < 0 ? 0 : weight;
|
|
return Math.round((zeroNegatives / totalWeight) * numberOfPlayers);
|
|
});
|
|
|
|
return normalized;
|
|
}
|
|
|
|
showQuestion(question) {
|
|
const { questionNumber, questionId, question: text, answers } = question;
|
|
|
|
if (this.botClient) {
|
|
const channel = this.botClient.channels.get(BOT_DISCORD_CHANNEL);
|
|
if (channel) {
|
|
channel.send({
|
|
embed: {
|
|
color: 3447003,
|
|
title: `Question #${questionNumber}`,
|
|
description: text,
|
|
fields: [
|
|
{
|
|
name: 'Choice 1',
|
|
value: answers[0].text,
|
|
},
|
|
{
|
|
name: 'Choice 2',
|
|
value: answers[1].text,
|
|
},
|
|
{
|
|
name: 'Choice 3',
|
|
value: answers[2].text,
|
|
},
|
|
],
|
|
},
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
showSummary(questionSummary, answerIndex) {
|
|
const { answerCounts, advancingPlayersCount, eliminatedPlayersCount, question } = questionSummary;
|
|
const { answerId } = answerCounts[answerIndex];
|
|
|
|
let choice1 = 'Choice 1';
|
|
let choice2 = 'Choice 2';
|
|
let choice3 = 'Choice 3';
|
|
|
|
if (answerCounts[0].answerId === answerId) choice1 = `${choice1} :ballot_box_with_check:`;
|
|
if (answerCounts[1].answerId === answerId) choice2 = `${choice2} :ballot_box_with_check:`;
|
|
if (answerCounts[2].answerId === answerId) choice3 = `${choice3} :ballot_box_with_check:`;
|
|
|
|
if (this.botClient) {
|
|
const channel = this.botClient.channels.get(BOT_DISCORD_CHANNEL);
|
|
if (channel) {
|
|
channel.send({
|
|
embed: {
|
|
color: 3447003,
|
|
title: `Summary`,
|
|
description: question,
|
|
fields: [
|
|
{
|
|
name: choice1,
|
|
value: answerCounts[0].text || answerCounts[0].answer || 'undefined',
|
|
},
|
|
{
|
|
name: choice2,
|
|
value: answerCounts[1].text || answerCounts[1].answer || 'undefined',
|
|
},
|
|
{
|
|
name: choice3,
|
|
value: answerCounts[2].text || answerCounts[2].answer || 'undefined',
|
|
},
|
|
],
|
|
footer: {
|
|
text: `${eliminatedPlayersCount} players eliminated / ${advancingPlayersCount} players advancing`,
|
|
},
|
|
},
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
scoreUsers(answerIndex) {
|
|
const correctAnswer = answerIndex + 1;
|
|
this.answers = this.answers.map(userAnswer => {
|
|
const { answer, userId } = userAnswer;
|
|
const { pre, number } = answer;
|
|
const negated = pre ? pre === 'not' : false;
|
|
let correct = false;
|
|
|
|
if (negated) {
|
|
correct = number !== correctAnswer.toString();
|
|
} else {
|
|
correct = number === correctAnswer.toString();
|
|
}
|
|
|
|
if (!this.players[userId]) this.players[userId] = 1;
|
|
if (correct) {
|
|
this.players[userId] += 1;
|
|
} else {
|
|
this.players[userId] -= 5;
|
|
}
|
|
|
|
return {
|
|
...userAnswer,
|
|
correct,
|
|
}
|
|
});
|
|
}
|
|
|
|
scrapeContents(msg) {
|
|
const { channel, guild } = msg;
|
|
|
|
if (!guild || !channel) return;
|
|
|
|
const validServer = servers.find(({ id }) => id === guild.id);
|
|
if (!validServer) return;
|
|
|
|
const validChannel = validServer.channels.find(({ id }) => id === channel.id);
|
|
if (!validChannel) return;
|
|
|
|
const { author, content } = msg;
|
|
const { id: userId, username } = author;
|
|
|
|
const cleanedMessage = content.trim().replace(' ', '').replace('\'', '').toLowerCase();
|
|
const matchedMessage = /((w|not|its)?([123])(\?|apg|apb)?)/g.exec(cleanedMessage);
|
|
|
|
if (!matchedMessage || matchedMessage.length === 0) return;
|
|
|
|
const fullAnswer = matchedMessage[0];
|
|
const answer = {
|
|
pre: matchedMessage[2],
|
|
number: matchedMessage[3],
|
|
post: matchedMessage[4],
|
|
};
|
|
|
|
let weight = this.weightAnswer(userId, answer);
|
|
weight = weight * validChannel.weightModifier;
|
|
weight = weight * this.ticker;
|
|
|
|
this.logAnswer(userId, username, answer, weight);
|
|
|
|
/*
|
|
This is for debugging. You don't want this during game.
|
|
console.log(`\n${validServer.name} => #${validChannel.name}`);
|
|
console.log(`${username}: ${fullAnswer}`);
|
|
console.log(`This answer carries weight modifier: ${weight}`);
|
|
*/
|
|
}
|
|
|
|
weightAnswer(userId, answer) {
|
|
let weight = 0;
|
|
const { pre, post } = answer;
|
|
const player = this.players[userId];
|
|
|
|
if (player) {
|
|
weight += parseInt(player);
|
|
}
|
|
|
|
if (pre) {
|
|
weight += weights[pre] || 0;
|
|
}
|
|
|
|
if (post) {
|
|
weight += weights[post] || 0;
|
|
}
|
|
|
|
if (!pre && !post) {
|
|
weight += weights['standard'];
|
|
}
|
|
|
|
return weight;
|
|
}
|
|
|
|
logAnswer(userId, username, answer, weight) {
|
|
const userAnswer = {
|
|
userId,
|
|
username,
|
|
answer: {
|
|
...answer,
|
|
},
|
|
};
|
|
|
|
const { number } = answer;
|
|
if (['1','2','3'].includes(number)) {
|
|
this.weights[number - 1] += weight;
|
|
}
|
|
|
|
this.answers.push(userAnswer);
|
|
}
|
|
|
|
saveContents() {
|
|
fs.readFile('playerAnswers.json', 'utf8', (err, data) => {
|
|
if (err) {
|
|
console.log('Error reading playerAnswers for writing: ', err);
|
|
} else {
|
|
try{
|
|
const playerAnswers = JSON.parse(data);
|
|
|
|
playerAnswers.playerScores = this.players;
|
|
|
|
if (!playerAnswers.answers) playerAnswers.answers = {};
|
|
playerAnswers.answers[this.questionId] = this.answers;
|
|
|
|
const playerAnswersJSON = JSON.stringify(playerAnswers, null, 2);
|
|
|
|
fs.writeFile('playerAnswers.json', playerAnswersJSON, 'utf8', (err, data) => {
|
|
if (err) {
|
|
console.log('Error saving playerAnswers: ', err);
|
|
} else {
|
|
// console.log(`Saved results for question ${TEST_QUESTION_ID.toString()}`);
|
|
}
|
|
this.answers = [];
|
|
});
|
|
}
|
|
catch(error){
|
|
console.warn(error);
|
|
}
|
|
|
|
}
|
|
});
|
|
}
|
|
}
|