Files
HQNode/scrapeDiscord.js
2020-06-23 23:27:27 -05:00

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);
}
}
});
}
}