250 lines
9.0 KiB
JavaScript
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);
|