Files
HQNode/hqnode.js
2018-06-29 23:56:24 -04:00

250 lines
9.0 KiB
JavaScript

import prompt from 'prompt';
import request from 'request';
import WebSocket from 'ws';
import { mainBearerToken, players } from './config.json';
import Account from './classes/Account';
import Scraper from './scrapeDiscord.js';
const HOST = 'api-quiz.hype.space';
const INIT_TOKEN = mainBearerToken;
const headers = {
'x-hq-client': 'Android/1.12.2',
'content-type': 'application/json; charset=UTF-8',
'user-agent': 'okhttp/3.8.0',
};
const PLAY_USING_DISCORD = true;
const SECONDS_BUFFER = 1; // How many seconds left you want to give before discord answers are used.
let playersInGame = players.map((player) => new Account(player));
const scraper = new Scraper();
scraper.run();
const removePlayer = (userId) => {
playersInGame = playersInGame.filter((player) => player.userId !== userId);
};
const toUSD = (currency) => {
return currency.toLocaleString('en-US', { style: 'currency', currency: 'USD' });
};
request('https://api-quiz.hype.space/shows/now', (error, response, body) => {
const result = JSON.parse(body);
if (!result.active) {
console.log('Game Not Active; Using debug server instead...');
}
const { broadcastId = 45589, socketUrl = 'wss://hqecho.herokuapp.com/' } = result.broadcast || {};
const { prize } = result || {};
const prizeInUSD = toUSD(prize || 0);
console.log(`Tonight's prize is ${prizeInUSD}!`);
const options = {
headers: {
Authorization: `Bearer ${INIT_TOKEN}`,
},
};
let TIMEOUT = null;
const ping = (ws, reconnect) => {
try {
ws.ping();
TIMEOUT = setTimeout(() => {
ws.terminate();
console.log('\x1b[1m\x1b[31m%s\x1b[0m', 'Master connection closed. Attempting to reconnnect...');
reconnect();
}, 2000);
} catch (error) {
console.log('\x1b[1m\x1b[31m%s\x1b[0m', 'Master connection closed. Attempting to reconnnect...');
reconnect();
}
};
const pong = () => {
clearTimeout(TIMEOUT);
};
const connectPlayer = (player) => {
if (!player.socket) {
let pTimeout = null;
const playerOptions = {
headers: {
Authorization: `Bearer ${player.accessToken}`,
},
};
const pws = new WebSocket(socketUrl, playerOptions);
const ping = (ws, reconnect) => {
try {
ws.ping();
pTimeout = setTimeout(() => {
ws.terminate();
console.log('\x1b[1m\x1b[33m%s\x1b[0m', `Player ${player.username} connection closed. Attempting to reconnect...`);
reconnect();
}, 2000);
} catch (error) {
console.log('\x1b[1m\x1b[33m%s\x1b[0m', `Player ${player.username} connection closed. Attempting to reconnect...`);
reconnect();
}
};
const pong = () => {
clearTimeout(pTimeout);
};
pws.on('pong', pong);
pws.on('open', () => {
setInterval(() => ping(pws, () => connectPlayer(player)), 10000);
console.log('\x1b[1m\x1b[36m%s\x1b[0m', `\nPlayer ${player.username} has connected to websocket`);
pws.send(JSON.stringify({ type:'subscribe', broadcastId }), () => {
console.log('\x1b[36m%s\x1b[0m', `\tPlayer ${player.username} has subscribed to game`);
player.activate(broadcastId, pws);
});
});
}
};
const connect = () => {
const ws = new WebSocket(socketUrl, options);
ws.on('error', (err) => {
console.log('An error occurred in the WebSocket: ', err);
});
ws.on('pong', () => pong(TIMEOUT));
ws.on('open', () => {
setInterval(() => ping(ws, connect), 10000);
console.log('\x1b[1m\x1b[32m%s\x1b[0m', 'Master connected to websocket')
playersInGame.forEach((player) => {
connectPlayer(player);
});
});
ws.on('message', (data) => {
const { answers = [], type = null } = JSON.parse(data);
// check if data type is a question not just like chat messages
if (type === 'questionFinished') {
console.log(`You have ${playersInGame.length} players still in game.`);
}
if (type === 'questionSummary') {
const questionSummary = JSON.parse(data);
const { answerCounts, advancingPlayersCount } = questionSummary;
const index = answerCounts.findIndex(answer => answer.correct);
const correctAnswer = answerCounts[index];
scraper.endQuestion(questionSummary, index);
const wrongPlayers = [];
playersInGame.forEach((player) => {
if (player.lastAnswerId !== correctAnswer.answerId) {
//console.log(`Player ${player.username} has been eliminated.`);
wrongPlayers.push(player.userId);
}
});
const answerText = correctAnswer.text || correctAnswer.answer || 'undefined';
console.log('\x1b[1m\x1b[32m%s\x1b[0m', `\nAnswer was ${index + 1}: ${answerText}`);
if (wrongPlayers.length === playersInGame.length) {
console.log('\x1b[1m\x1b[31m%s\x1b[0m', `All remaining ${wrongPlayers.length} players have been eliminated!`);
} else if (wrongPlayers.length > 0) {
console.log('\x1b[1m\x1b[33m%s\x1b[0m', `${wrongPlayers.length} players have been eliminated!`);
} else if (playersInGame.length > 0) {
console.log('\x1b[1m\x1b[32m%s\x1b[0m', 'No players have been eliminated!');
}
if (advancingPlayersCount && advancingPlayersCount > 0) {
const potentialWinnings = (prize / advancingPlayersCount);
const potentialPerPlayer = toUSD(potentialWinnings);
const potentialTotal = toUSD(potentialWinnings * (playersInGame.length - wrongPlayers.length));
console.log(`\nPotential winnings: ${potentialTotal} (${potentialPerPlayer} per player remaining)`);
}
wrongPlayers.forEach(userId => removePlayer(userId));
}
if (type !== 'question') return;
const { questionNumber, question: theQuestion, questionId, timeLeftMs } = JSON.parse(data);
let timesUp = false;
let timeoutTimer = null;
if (PLAY_USING_DISCORD) {
timeoutTimer = setTimeout(() => {
timesUp = true;
console.log('\nTime buffer expired. Playing using Discord...');
let weights = scraper.getWeights();
console.log(`Weights: [${weights}]`);
const normalized = scraper.normalizeWeights(playersInGame.length);
let playerIndex = 0;
normalized.forEach((playersAssignedToAnswer, index) => {
for (let i = 0; i < playersAssignedToAnswer; i++) {
const player = playersInGame[playerIndex];
if (player) {
player.sendAnswer(questionId, answers[index].answerId);
console.log(`Player ${player.username} sent answer ${index + 1}`);
playerIndex++;
}
}
});
}, timeLeftMs - SECONDS_BUFFER * 1000);
}
scraper.startQuestion(JSON.parse(data));
console.log('\n==================================\n');
console.log(`Question #${questionNumber} with ${Math.floor(timeLeftMs / 1000)} seconds left:`);
console.log(`\n${theQuestion}`);
console.log(`\nChoice 1: ${answers[0].text}`);
console.log(`Choice 2: ${answers[1].text}`);
console.log(`Choice 3: ${answers[2].text}\n`);
if (playersInGame.length > 0) {
prompt.start();
prompt.get(['answer'], (err, res) => {
console.log();
if (!timesUp) {
if (timeoutTimer) clearTimeout(timeoutTimer);
const inputAnswer = res.answer.trim();
if (!inputAnswer) {
playersInGame.forEach((player, index) => {
console.log(`Player ${player.username} sent answer ${(index % 3) + 1}`);
player.sendAnswer(questionId, answers[index % 3].answerId);
});
} else {
const input = inputAnswer.split(/,| |;/);
switch (input.length) {
case 1:
playersInGame.forEach((player, index) => {
console.log(`Player ${player.username} sent answer ${input[0]}`);
player.sendAnswer(questionId, answers[input[0] - 1].answerId);
});
break;
case 2:
playersInGame.forEach((player, index) => {
console.log(`Player ${player.username} sent answer ${input[index % 2]}`);
player.sendAnswer(questionId, answers[input[index % 2] - 1].answerId);
});
break;
case 3:
playersInGame.forEach((player, index) => {
console.log(`Player ${player.username} sent answer ${input[index % 3]}`);
player.sendAnswer(questionId, answers[input[index % 3] - 1].answerId);
});
break;
default:
console.log('ERROR: you sent too many answer choices');
break;
}
}
}
});
}
});
};
connect();
}).auth(null, null, true, INIT_TOKEN);