Initial Commit
This commit is contained in:
8
.eslintrc
Normal file
8
.eslintrc
Normal 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
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
node_modules
|
||||
test.js
|
||||
test2.js
|
||||
tokens/*
|
||||
3
config.json
Normal file
3
config.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"catchall": "spotimail.com"
|
||||
}
|
||||
28
package.json
Normal file
28
package.json
Normal 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
186
src/classes/Client.js
Normal 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
36
src/index.js
Normal 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
51
src/modules/hq-cashout.js
Normal 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;
|
||||
};
|
||||
0
src/modules/hq-checklives.js
Normal file
0
src/modules/hq-checklives.js
Normal file
44
src/modules/hq.js
Normal file
44
src/modules/hq.js
Normal 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
22
src/utils/selectTokens.js
Normal 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);
|
||||
};
|
||||
Reference in New Issue
Block a user