Initial Commit

This commit is contained in:
vilP1L
2019-05-13 20:33:24 -04:00
commit 49e097a6b1
12 changed files with 3921 additions and 0 deletions

3
.babelrc Normal file
View File

@@ -0,0 +1,3 @@
{
"presets": ["airbnb"]
}

8
.eslintrc Normal file
View File

@@ -0,0 +1,8 @@
{
"extends": "airbnb-base",
"rules": {
"no-console": 0,
"no-restricted-syntax": 0,
"no-await-in-loop": 0
}
}

4
.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
node_modules
test.js
test2.js
tokens/*

3
config.json Normal file
View File

@@ -0,0 +1,3 @@
{
"catchall": "spotimail.com"
}

28
package.json Normal file
View File

@@ -0,0 +1,28 @@
{
"name": "emu",
"version": "1.0.0",
"main": "src/index.js",
"repository": "https://github.com/vilP1l/emu",
"author": "vilP1L <vilP1l@github.com>",
"license": "MIT",
"private": true,
"dependencies": {
"colors": "^1.3.3",
"enquirer": "^2.3.0",
"faker": "^4.1.0",
"request": "^2.88.0",
"ws": "^7.0.0"
},
"devDependencies": {
"@babel/cli": "^7.4.4",
"@babel/core": "^7.4.4",
"@babel/node": "^7.2.2",
"babel-preset-airbnb": "^3.2.1",
"eslint": "^5.16.0",
"eslint-config-airbnb-base": "^13.1.0",
"eslint-plugin-import": "^2.17.2"
},
"scripts": {
"start": "babel-node src/index.js"
}
}

186
src/classes/Client.js Normal file
View File

@@ -0,0 +1,186 @@
import request from 'request';
import WebSocket from 'ws';
import { promisify } from 'util';
import 'colors';
import faker from 'faker';
request.promise = promisify(request);
export default class Client {
constructor(loginToken, index) {
if (!loginToken) throw new Error('No login token was provided.');
this.index = index || 0;
if (index === 0 || !index) this.master = true;
this.loginToken = loginToken;
this.headers = {
'x-hq-client': 'Android/1.28.2',
'user-agent': 'okhttp/3.8.0',
'x-hq-lang': 'en',
'x-hq-country': 'US',
'x-hq-stk': 'MQ==',
};
this.testWs = false;
this.logging = true;
this.debug = false;
this.inTheGame = true;
this.answers = [];
this.broadcastID = 1337;
this.questionID = 0;
this.erasedIndex = -1;
this.hasExtraLife = false;
this.hasEraser = false;
this.canCashout = false;
this.letter = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'[index % 26];
}
async login() {
const { body } = await request.promise('https://api-quiz.hype.space/tokens', {
method: 'POST',
json: { token: this.loginToken },
headers: this.headers,
});
const { error, accessToken } = body;
if (error) throw new Error(`Error while getting access token: ${error}`);
this.accessToken = accessToken;
this.headers = {
...this.headers,
authorization: `Bearer ${this.accessToken}`,
};
return this.accessToken;
}
async schedule() {
const { body } = await request.promise('https://api-quiz.hype.space/shows/now', {
headers: this.headers,
});
const json = JSON.parse(body);
if (json.active) this.broadcastID = json.broadcast.broadcastId;
return json;
}
async connect() {
if (this.testWs) {
this.ws = new WebSocket('wss://hqecho.herokuapp.com', {
headers: this.headers,
});
if (this.logging) console.log(`${` Client ${this.index} `.bgBlue.black}${' Using TestWs '.bgYellow.black}`);
} else {
const { broadcast, active } = await this.schedule();
if (this.logging && !active) return console.log(`${` Client ${this.index} `.bgBlue.black}${' Game Not Live '.bgRed.black}`);
this.ws = new WebSocket(broadcast.socketUrl, {
headers: this.headers,
});
}
this.ws.onopen = () => {
if (this.logging) console.log(`${` Client ${this.index} `.bgBlue.black}${' Connected '.bgGreen.black}`);
const i = setInterval(() => {
if (this.ws.readyState !== this.ws.OPEN) return clearInterval(i);
this.ws.ping();
if (this.debug) console.log(`${` Client ${this.index} `.bgBlue.black}${' PING '.bgGreen.black}`);
return true;
}, 5000);
this.subscribe();
};
this.ws.onclose = () => {
if (this.logging) console.log(`${` Client ${this.index} `.bgBlue.black}${' Disconnected '.bgRed.black}`);
};
this.ws.on('message', this.parse.bind(this));
return true;
}
subscribe() {
if (!this.ws || this.ws.readyState !== this.ws.OPEN) throw new Error('No WebSocket connection is active.');
this.ws.send(JSON.stringify({
type: 'subscribe',
broadcastId: this.broadcastID,
}));
if (this.logging) console.log(`${` Client ${this.index} `.bgBlue.black}${' Subscribed '.bgGreen.black}`);
}
parse(msg) {
const data = JSON.parse(msg);
switch (data.type) {
case 'question':
this.questionID = data.questionId;
this.answers = data.answers;
if (this.master) console.log(`${' HQ '.bgBlue.black}${` Question ${data.questionNumber} `.bgGreen.black}`);
break;
case 'questionSummary':
if (this.master) console.log(`${' HQ '.bgBlue.black}${' Question Summary '.bgGreen.black}`);
if (!this.inTheGame) return;
this.inTheGame = data.youGotItRight;
if (!this.inTheGame && this.logging) console.log(`${` Client ${this.index} `.bgBlue.black}${' Eliminated '.bgRed.black}`);
break;
case 'gameStatus':
if (!data.extraLivesRemaining) data.extraLivesRemaining = 0;
if (!data.erase1sRemaining) data.erase1sRemaining = 0;
this.hasEraser = data.erase1sRemaining > 0;
this.hasExtraLife = data.extraLivesRemaining > 0;
break;
case 'erase1Answer':
this.erasedIndex = this.answers.findIndex(({ answerId }) => answerId === data.answerId);
if (this.logging) console.log(`${` Client ${this.index} `.bgBlue.black}${` Erased choice ${this.erasedIndex + 1} `.bgGreen.black}`);
break;
default:
break;
}
}
submitAnswer(index) {
if (!this.inTheGame) return;
if (!this.ws || this.ws.readyState !== this.ws.OPEN) throw new Error('No WebSocket connection is active.');
if (index > 2 || index < 0) return;
const { answerId } = this.answers[index];
if (!answerId) return;
this.ws.send(JSON.stringify({
type: 'answer',
broadcastId: this.broadcastID,
questionId: this.questionID,
answerId,
}));
if (this.logging) console.log(`${` Client ${this.index} `.bgBlue.black}${` Chose option ${index + 1} `.bgGreen.black}`);
}
useExtraLife() {
if (!this.ws || this.ws.readyState !== this.ws.OPEN) throw new Error('No WebSocket connection is active.');
if (!this.hasExtraLife) return;
this.ws.send(JSON.stringify({
type: 'useExtraLife',
broadcastId: this.broadcastID,
questionId: this.questionID,
}));
this.inTheGame = true;
if (this.logging) console.log(`${` Client ${this.index} `.bgBlue.black}${' Used Extra Life '.bgGreen.black}`);
}
useEraser() {
if (!this.ws || this.ws.readyState !== this.ws.OPEN) throw new Error('No WebSocket connection is active.');
if (!this.hasEraser) return;
this.socket.send(JSON.stringify({ type: 'erase1', questionId: this.questionID, broadcastId: this.broadcastID }));
}
async getBalance() {
const { body } = await request.promise('https://api-quiz.hype.space/users/me/payouts', {
headers: this.headers,
});
const { balance } = JSON.parse(body);
this.canCashout = balance.eligibleForPayout;
return { balance: Number(balance.unpaid.replace('$', '')), eligibleForPayout: balance.eligibleForPayout };
}
async cashout(catchall) {
if (!catchall) throw new Error('No catchall was provided.');
this.email = `${faker.internet.userName()}@${catchall}`;
const { body } = await request.promise('https://api-quiz.hype.space/users/me/payouts', {
method: 'POST',
headers: this.headers,
form: { email: this.email },
});
if (!body) return console.log(`${` Client ${this.index} `.bgBlue.black}${' Error while cashing out: No response was recieved from HQ. '.bgRed.black}`);
if (body.includes('<html>') && this.logging) console.log(`${` Client ${this.index} `.bgBlue.black}${' Error while cashing out: HTML was returned. '.bgRed.black}`);
const { error } = JSON.parse(body);
if (error && this.logging) return console.log(`${` Client ${this.index} `.bgBlue.black}${` Error while cashing out: ${error}. `.bgRed.black}`);
if (this.logging) console.log(`${` Client ${this.index} `.bgBlue.black}${` Successfully cashed out to ${this.email}! `.bgGreen.black}`);
return true;
}
}

36
src/index.js Normal file
View File

@@ -0,0 +1,36 @@
import { prompt } from 'enquirer';
// MODULE IMPORTS
import hqMain from './modules/hq';
import hqCashout from './modules/hq-cashout';
import hqCheckLives from './modules/hq-checklives';
(async () => {
process.title = 'Australian Emu Farm Suite v1';
const { selection } = await prompt({
type: 'autocomplete',
name: 'selection',
message: 'Australian Emu Farm Suite v1 Startup Menu',
suggest(input, choices) {
return choices.filter(choice => choice.message.toLowerCase().includes(input));
},
choices: [
'HQ: Start',
'HQ: Cashout',
'HQ: Check Lives',
'HQ: Make it rain',
],
});
switch (selection) {
case 'HQ: Start':
hqMain();
break;
case 'HQ: Cashout':
hqCashout();
break;
case 'HQ: Check Lives':
hqCheckLives();
break;
default:
break;
}
})();

51
src/modules/hq-cashout.js Normal file
View File

@@ -0,0 +1,51 @@
import { prompt } from 'enquirer';
import Client from '../classes/Client';
import select from '../utils/selectTokens';
import { catchall } from '../../config';
console.edit = (string, end) => process.stdout.clearLine() || process.stdout.write(`\r${string}${end ? '\n' : ''}`);
export default async () => {
const tokens = await select();
const clients = [];
let unlockedCount = 0;
let unlockedBalance = 0;
let lockedCount = 0;
let lockedBalance = 0;
let noBalanceCount = 0;
let index = 0;
for (const token of tokens) {
index += 1;
const client = new Client(token.loginToken, index - 1);
await client.login();
clients.push(client);
const { balance, eligibleForPayout } = await client.getBalance();
if (balance === 0) noBalanceCount += 1;
if (eligibleForPayout) {
unlockedCount += 1;
unlockedBalance += balance;
} else {
lockedCount += 1;
lockedBalance += balance;
}
console.edit(`Checked ${index}/${tokens.length} accounts.`.yellow);
}
console.edit(`Finished checking ${tokens.length} accounts!`.green, true);
console.log(`${' Unlocked Balance '.bgGreen.black} $${unlockedBalance.toFixed(2)}`);
console.log(`${' Unlocked Accounts '.bgGreen.black} ${unlockedCount}`);
console.log(`${' Locked Balance '.bgRed.black} $${lockedBalance.toFixed(2)}`);
console.log(`${' Locked Accounts '.bgRed.black} ${lockedCount}`);
console.log(`${' No Balance '.bgYellow.black} ${noBalanceCount}`);
const { num } = await prompt({
type: 'numeral',
name: 'num',
message: 'How many accounts would you like to cashout?',
});
const accounts = clients.filter(c => c.canCashout);
if (num > accounts.length) return console.log(`Only ${accounts.length} account(s) are able to cashout.`.red);
for (let i = 0; i < num; i += 1) {
await accounts[i].cashout(catchall);
}
console.log(`Finished cashing out ${num} account(s)!`.green);
return true;
};

View File

44
src/modules/hq.js Normal file
View File

@@ -0,0 +1,44 @@
import { prompt } from 'enquirer';
import Client from '../classes/Client';
import select from '../utils/selectTokens';
export default async () => {
const tokens = await select();
const { numClients } = await prompt({
type: 'numeral',
name: 'numClients',
message: 'How many clients would you like to use?',
});
const { useTestWs } = await prompt({
type: 'confirm',
name: 'useTestWs',
message: 'Would you like to use the test WebSocket?',
});
const master = new Client(tokens[0].loginToken, 0);
const clients = [];
master.testWs = useTestWs;
tokens.shift();
await master.login();
await master.connect();
master.ws.on('message', async (msg) => {
const { type, answers } = JSON.parse(msg);
if (type === 'question') {
const { choice } = await prompt({
type: 'select',
name: 'choice',
message: 'Please choose an option',
choices: [`1 - ${answers[0].text}`, `2 - ${answers[1].text}`, `3 - ${answers[2].text}`],
});
const num = Number(choice.match(/[1-9]/)[0]);
master.submitAnswer(num - 1);
clients.forEach(c => c.submitAnswer(num - 1));
}
});
for (let i = 0; i < numClients; i += 1) {
const client = new Client(tokens[i].loginToken, i + 1);
await client.login();
client.testWs = useTestWs;
await client.connect();
clients.push(client);
}
};

22
src/utils/selectTokens.js Normal file
View File

@@ -0,0 +1,22 @@
import { prompt } from 'enquirer';
import fs from 'fs';
export default async () => {
const files = fs.readdirSync('./tokens');
if (!files.length) throw new Error('No tokens in token directory.');
if (files.length === 1) {
const contents = fs.readFileSync(`./tokens/${files[0]}`, 'UTF8');
return JSON.parse(contents);
}
const { file } = await prompt({
type: 'autocomplete',
name: 'file',
message: 'Please Choose a Token Set.',
suggest(input, choices) {
return choices.filter(choice => choice.message.startsWith(input));
},
choices: files,
});
const contents = fs.readFileSync(`./tokens/${file}`, 'UTF8');
return JSON.parse(contents);
};

3536
yarn.lock Normal file

File diff suppressed because it is too large Load Diff