// ========== CONFIG ========== const BASE_URL = 'https://hl3countdown.com/api'; const ACCOUNTS = [ { name: 'Account 1', headers: { 'Cookie': 'identity=', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', } } ]; const FIXED_PRICE = 1000; // Fixed price for buying stocks // ========== HELPERS ========== async function doJsonFetch(url, options = {}) { const res = await fetch(url, { ...options, }); let bodyText = ''; try { bodyText = await res.text(); } catch { bodyText = ''; } let json; try { json = bodyText ? JSON.parse(bodyText) : null; } catch (e) { json = null; } if (!res.ok) { throw new Error( `HTTP ${res.status} ${res.statusText} for ${url}. Body: ${bodyText}` ); } return json; } // Compute quantity from total_score (thousands, capped at 5000) function computeQuantity(total_score) { if (typeof total_score !== 'number') { throw new Error(`total_score is not a number: ${total_score}`); } const pointsK = Math.floor(total_score / 1000); return Math.min(pointsK, 5000); } // Try to extract orderId from buy response function extractOrderId(buyResponse) { if (!buyResponse) return null; if (typeof buyResponse.orderId === 'number' || typeof buyResponse.orderId === 'string') { return buyResponse.orderId; } if (buyResponse.order && (buyResponse.order.id || buyResponse.order.orderId)) { return buyResponse.order.id || buyResponse.order.orderId; } if (buyResponse.id) { return buyResponse.id; } return null; } // ========== PER-ACCOUNT FLOW ========== async function processAccount(account, isFirstRun) { const { name, headers } = account; console.log(`\n=== Processing ${name} ===`); // 0. Set username on first run if (isFirstRun) { const accountNumber = name.match(/\d+/)?.[0] || '0'; const setUsernameUrl = `https://hl3countdown.com/leaderboard/set-username`; const num = parseInt(accountNumber); const isOdd = num % 2 === 1; const username = isOdd ? "pee" : "poo"; const usernameBody = { username: username, }; console.log(`[${name}] POST ${setUsernameUrl} with body`, usernameBody); try { const usernameResponse = await doJsonFetch(setUsernameUrl, { method: 'POST', headers: { ...headers, 'Content-Type': 'application/json', 'Accept': 'application/json, text/plain, */*', }, body: JSON.stringify(usernameBody), }); console.log(`[${name}] ✓ Username set successfully to: ${username}`); console.log(`[${name}] Set username response:`, usernameResponse); } catch (err) { console.error(`[${name}] ✗ Failed to set username:`, err.message || err); } } // 1. GET leaderboard/user to fetch total_score const leaderboardUrl = `${BASE_URL}/leaderboard/user`; console.log(`[${name}] GET ${leaderboardUrl}`); const userData = await doJsonFetch(leaderboardUrl, { method: 'GET', headers, }); if (userData == null) { throw new Error(`[${name}] Empty response from leaderboard/user`); } const total_score = userData.total_score; console.log(`[${name}] total_score = ${total_score}`); const quantity = computeQuantity(total_score); console.log(`[${name}] computed quantity = ${quantity}`); if (quantity <= 0) { console.log(`[${name}] quantity <= 0, skipping buy.`); return; } // 2. POST stocks/hlx/buy // If total_score < 1000, use total_score as price; otherwise use FIXED_PRICE const price = FIXED_PRICE; const buyUrl = `${BASE_URL}/stocks/hlx/buy`; const buyBody = { quantity, price, }; console.log(`[${name}] POST ${buyUrl} with body`, buyBody); const buyResponse = await doJsonFetch(buyUrl, { method: 'POST', headers: { ...headers, 'Content-Type': 'application/json', 'Accept': 'application/json, text/plain, */*', }, body: JSON.stringify(buyBody), }); console.log(`[${name}] Buy response:`, buyResponse); // 3. Extract orderId const orderId = extractOrderId(buyResponse); if (!orderId) { console.warn(`[${name}] Could not find orderId in buy response, not deleting.`); return; } // 4. DELETE stocks/orders/buy/{orderId} const deleteUrl = `${BASE_URL}/stocks/orders/buy/${orderId}`; console.log(`[${name}] DELETE ${deleteUrl}`); const deleteResponse = await doJsonFetch(deleteUrl, { method: 'DELETE', headers, }); console.log(`[${name}] Delete response:`, deleteResponse); // 5. Roulette spin 100 times const accountNumber = parseInt(name.match(/\d+/)?.[0] || '1'); const betType = accountNumber % 2 === 1 ? 'red' : 'black'; for (let i = 0; i < 100; i++) { const rouletteBody = { bets: [{ type: betType, amount: 1e-17 }] }; const rouletteUrl = `https://hl3countdown.com/api/roulette/spin`; console.log(`[${name}] Spin ${i + 1}/100 - POST ${rouletteUrl}`); try { const rouletteResponse = await doJsonFetch(rouletteUrl, { method: 'POST', headers: { ...headers, 'Content-Type': 'application/json', 'Accept': 'application/json, text/plain, */*', }, body: JSON.stringify(rouletteBody), }); // Collect result for table if (rouletteResponse) { rouletteResults.push({ account: name, accountNumber: accountNumber, spinNumber: i + 1, winningNumber: rouletteResponse.winningNumber, color: rouletteResponse.color, betType: rouletteBody.bets[0].type, betAmount: rouletteBody.bets[0].amount, won: rouletteResponse.bets[0].won, payout: rouletteResponse.totalPayout, netChange: rouletteResponse.netChange }); } } catch (err) { console.error(`[${name}] Spin ${i + 1} failed:`, err.message || err); } } console.log(`[${name}] Completed 100 spins`); } // ========== MAIN ========== // Global array to collect roulette results const rouletteResults = []; async function main() { const fs = require('fs'); const path = require('path'); const flagFile = path.join(__dirname, '.first_run_complete'); const isFirstRun = !fs.existsSync(flagFile); // First, fetch balances for all accounts console.log('=== Fetching account balances ==='); const accountBalances = []; for (const account of ACCOUNTS) { try { const leaderboardUrl = `${BASE_URL}/leaderboard/user`; const userData = await doJsonFetch(leaderboardUrl, { method: 'GET', headers: account.headers, }); const total_score = userData?.total_score || 0; accountBalances.push({ account, total_score }); console.log(`${account.name}: ${total_score}`); } catch (err) { console.error(`Failed to fetch balance for ${account.name}:`, err.message); accountBalances.push({ account, total_score: 0 }); } } // Sort by balance (highest to lowest) accountBalances.sort((a, b) => b.total_score - a.total_score); // Rename accounts: highest = Account 1, lowest = Account 10 accountBalances.forEach((item, index) => { const newNumber = index + 1; item.account.name = `Account ${newNumber}`; }); console.log('\n=== Accounts renamed by balance (highest to lowest) ==='); accountBalances.forEach(item => { console.log(`${item.account.name}: ${item.total_score}`); }); try { await Promise.all( ACCOUNTS.map((account) => processAccount(account, isFirstRun).catch((err) => { console.error(`Error in ${account.name}:`, err.message || err); }) ) ); if (isFirstRun) { fs.writeFileSync(flagFile, new Date().toISOString()); console.log('\nFirst run complete. Usernames set.'); } console.log('\nAll accounts processed.'); // Display results table if (rouletteResults.length > 0) { let output = ''; output += '\n\n=== ROULETTE RESULTS TABLE (100 SPINS PER ACCOUNT) ===\n'; output += '┌─────────────┬────────┬───────┬────────────┬───────┬────────────┬─────┬─────────┬────────────┐\n'; output += '│ Account │ Acc # │ Spin │ Landed On │ Color │ Bet Type │ Won │ Payout │ Net Change │\n'; output += '├─────────────┼────────┼───────┼────────────┼───────┼────────────┼─────┼─────────┼────────────┤\n'; rouletteResults.forEach(result => { const account = result.account.padEnd(11); const accNum = String(result.accountNumber).padStart(6); const spin = String(result.spinNumber).padStart(5); const number = String(result.winningNumber).padStart(10); const color = result.color.padEnd(5); const betType = result.betType.padEnd(10); const won = (result.won ? '✓' : '✗').padEnd(3); const payout = String(result.payout).padStart(7); const netChange = String(result.netChange).padStart(10); output += `│ ${account} │ ${accNum} │ ${spin} │ ${number} │ ${color} │ ${betType} │ ${won} │ ${payout} │ ${netChange} │\n`; }); output += '└─────────────┴────────┴───────┴────────────┴───────┴────────────┴─────┴─────────┴────────────┘\n'; // Summary statistics const totalWins = rouletteResults.filter(r => r.won).length; const totalLosses = rouletteResults.filter(r => !r.won).length; const totalNetChange = rouletteResults.reduce((sum, r) => sum + r.netChange, 0); output += '\n=== SUMMARY ===\n'; output += `Total Spins: ${rouletteResults.length}\n`; output += `Wins: ${totalWins} (${((totalWins / rouletteResults.length) * 100).toFixed(1)}%)\n`; output += `Losses: ${totalLosses} (${((totalLosses / rouletteResults.length) * 100).toFixed(1)}%)\n`; output += `Total Net Change: ${totalNetChange}\n`; // Number frequency output += '\n=== NUMBER FREQUENCY ===\n'; const numberFreq = {}; rouletteResults.forEach(r => { numberFreq[r.winningNumber] = (numberFreq[r.winningNumber] || 0) + 1; }); Object.entries(numberFreq) .sort((a, b) => b[1] - a[1]) .forEach(([num, count]) => { output += `Number ${num}: ${count} time(s)\n`; }); // Color distribution output += '\n=== COLOR DISTRIBUTION ===\n'; const colorFreq = {}; rouletteResults.forEach(r => { colorFreq[r.color] = (colorFreq[r.color] || 0) + 1; }); Object.entries(colorFreq) .sort((a, b) => b[1] - a[1]) .forEach(([color, count]) => { output += `${color}: ${count} time(s) (${((count / rouletteResults.length) * 100).toFixed(1)}%)\n`; }); // Print to console console.log(output); // Write to file in results directory const resultsDir = path.join(__dirname, 'results'); if (!fs.existsSync(resultsDir)) { fs.mkdirSync(resultsDir, { recursive: true }); } const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); const filename = path.join(resultsDir, `roulette_results_${timestamp}.txt`); fs.writeFileSync(filename, output, 'utf8'); console.log(`\n✓ Results written to: ${filename}`); } } catch (err) { console.error('Fatal error:', err); } } main();