multibot / bot.js
gi2h270's picture
Update bot.js
a155e62 verified
import { chromium } from 'playwright-extra';
import StealthPlugin from 'puppeteer-extra-plugin-stealth';
import fs from 'fs/promises';
import { getRecaptchaToken } from './solver.js';
chromium.use(StealthPlugin());
// ========== KONFIGURASI (sesuai userscript) ==========
const CONFIG = {
email: "gi2h2705@gmail.com", // πŸ” GANTI DENGAN EMAIL TERDAFTAR
baseUrl: "https://litecoin.freepepecoin.com",
maxCycles: 100000,
debug: true
};
const SITE_KEY = '6LcbMB0sAAAAAAxsy76NqLNBhHfzZO8E4jLJ8XNl';
// ========== DATA COIN ==========
const COINS = {
LTC: { name: 'Litecoin', urlPath: '/faucet/LTC', defaultCooldown: 180, cooldown: 180, lastClaim: 0, successCount: 0, totalClaims: 0, totalRewards: 0 },
DOGE: { name: 'Dogecoin', urlPath: '/faucet/DOGE', defaultCooldown: 30, cooldown: 30, lastClaim: 0, successCount: 0, totalClaims: 0, totalRewards: 0 },
TRUMP: { name: 'Trumpcoin', urlPath: '/faucet/TRUMP', defaultCooldown: 10, cooldown: 10, lastClaim: 0, successCount: 0, totalClaims: 0, totalRewards: 0 }
};
class MultiCoinFaucetBot {
constructor() {
this.email = CONFIG.email;
this.baseUrl = CONFIG.baseUrl;
this.siteKey = SITE_KEY;
this.isRunning = false;
this.totalCycles = 0;
this.consecutiveFailures = 0;
this.maxConsecutiveFailures = 5;
this.cookies = {};
this.csrfTokens = {};
this.COINS = JSON.parse(JSON.stringify(COINS)); // deep copy
this.browser = null;
this.page = null;
this.loadStats();
}
// ========== LOGGING ==========
log(message, color = 'info') {
const timestamp = new Date().toLocaleTimeString();
console.log(`[${timestamp}] ${message}`);
}
// ========== PERSISTENSI via FILE ==========
async loadStats() {
try {
const data = await fs.readFile('stats.json', 'utf-8');
const stats = JSON.parse(data);
this.totalCycles = stats.totalCycles || 0;
this.consecutiveFailures = stats.consecutiveFailures || 0;
if (stats.coins) {
Object.keys(this.COINS).forEach(sym => {
if (stats.coins[sym]) Object.assign(this.COINS[sym], stats.coins[sym]);
});
}
this.log('[stats] βœ… Dimuat');
} catch {
this.log('[stats] ℹ️ Statistik baru');
}
}
async saveStats() {
try {
const stats = {
totalCycles: this.totalCycles,
consecutiveFailures: this.consecutiveFailures,
coins: this.COINS,
email: this.email
};
await fs.writeFile('stats.json', JSON.stringify(stats, null, 2));
this.log('[stats] πŸ’Ύ Tersimpan');
} catch (e) {
this.log(`[stats] ⚠️ Gagal simpan: ${e.message}`);
}
}
async saveCookies() {
try {
const cookies = await this.page.context().cookies();
await fs.writeFile('cookies.json', JSON.stringify(cookies, null, 2));
this.log('[cookies] πŸ’Ύ Tersimpan');
} catch (e) {
this.log(`[cookies] ⚠️ Gagal simpan: ${e.message}`);
}
}
async loadCookies(context) {
try {
const cookies = JSON.parse(await fs.readFile('cookies.json', 'utf-8'));
await context.addCookies(cookies);
this.log('[cookies] βœ… Dimuat');
} catch {
this.log('[cookies] ℹ️ Tidak ada cookies');
}
}
// ========== SOLVER EKSTERNAL ==========
async getRecaptchaToken() {
this.log('[solver] 🧩 Meminta token reCAPTCHA...');
try {
const token = await getRecaptchaToken(this.baseUrl, this.siteKey);
return token;
} catch (e) {
this.log(`[solver] ❌ Gagal: ${e.message}`);
throw e;
}
}
// ========== LOGIN (fetch di dalam page) ==========
async login() {
this.log('\n[login] πŸ” Memulai login...');
try {
await this.page.goto(this.baseUrl, { waitUntil: 'networkidle' });
const token = await this.getRecaptchaToken();
// βœ… FETCH POST – sama persis userscript
const success = await this.page.evaluate(
async (baseUrl, email, token) => {
const response = await fetch(`${baseUrl}/`, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: `address=${encodeURIComponent(email)}&g-recaptcha-response=${token}`,
credentials: 'include'
});
const text = await response.text();
return text.includes(`Welcome, <b>${email}</b>`);
},
this.baseUrl,
this.email,
token
);
if (success) {
this.log('[login] βœ… BERHASIL login');
await this.saveCookies();
return true;
} else {
this.log('[login] ❌ Gagal login');
return false;
}
} catch (e) {
this.log(`[login] ❌ Error: ${e.message}`);
return false;
}
}
// ========== CSRF TOKEN (Playwright) ==========
async getCsrfToken(coinSymbol) {
const coin = this.COINS[coinSymbol];
this.log(`[csrf] Mengambil token ${coinSymbol}...`);
try {
await this.page.goto(`${this.baseUrl}${coin.urlPath}`, { waitUntil: 'networkidle' });
await this.page.waitForSelector('input[name="csrf_token"]', { timeout: 10000 });
const csrf = await this.page.inputValue('input[name="csrf_token"]');
this.csrfTokens[coinSymbol] = csrf;
this.log(`[csrf] βœ… Ditemukan: ${csrf.substring(0, 10)}...`);
return csrf;
} catch (e) {
this.log(`[csrf] ❌ Gagal: ${e.message}`);
return null;
}
}
// ========== EXTRACT COOLDOWN (sama persis userscript) ==========
extractCooldownFromResponse(text) {
try {
const patterns = [
{ regex: /cooldown.*?(\d+)\s*(minute|min|second|sec|hour|hr)/gi },
{ regex: /wait.*?(\d+)\s*(minute|min|second|sec|hour|hr)/gi },
{ regex: /timer.*?(\d+)\s*(minute|min|second|sec|hour|hr)/gi },
{ regex: /(\d+)\s*(minute|min|second|sec|hour|hr).*?cooldown/gi },
{ regex: /(\d+)\s*(minute|min|second|sec|hour|hr).*?wait/gi }
];
for (const pattern of patterns) {
const matches = text.match(pattern.regex);
if (matches) {
for (const match of matches) {
const numMatch = match.match(/(\d+)\s*(minute|min|second|sec|hour|hr)/i);
if (numMatch) {
const value = parseInt(numMatch[1]);
const unit = numMatch[2].toLowerCase();
if (unit.includes('hour') || unit.includes('hr')) return value * 3600;
if (unit.includes('minute') || unit.includes('min')) return value * 60;
if (unit.includes('second') || unit.includes('sec')) return value;
}
}
}
}
return null;
} catch (error) {
this.log(`⚠️ Error extracting cooldown: ${error.message}`, 'warning');
return null;
}
}
// ========== CLAIM COIN (fetch di page) ==========
async claimCoin(coinSymbol) {
const coin = this.COINS[coinSymbol];
this.log(`\n[claim] πŸͺ™ CLAIMING ${coinSymbol} (${coin.name})`);
// 1. Ambil CSRF token
const csrfToken = await this.getCsrfToken(coinSymbol);
if (!csrfToken) {
this.log(`❌ Gagal CSRF token untuk ${coinSymbol}`);
return { success: false };
}
// 2. Dapatkan token reCAPTCHA
this.log('[claim] 🧩 Meminta token reCAPTCHA...');
let recaptchaToken;
try {
recaptchaToken = await this.getRecaptchaToken();
} catch (e) {
this.log(`❌ Gagal token reCAPTCHA: ${e.message}`);
return { success: false };
}
try {
// βœ… FETCH POST β€” sama persis userscript
const result = await this.page.evaluate(
async (baseUrl, urlPath, csrf, token, symbol) => {
const response = await fetch(`${baseUrl}${urlPath}`, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: `currency=${symbol}&csrf_token=${csrf}&g-recaptcha-response=${token}&claim=`,
credentials: 'include'
});
const text = await response.text();
const lower = text.toLowerCase();
const success = ['successfully', 'claimed', 'reward', 'thank you', 'congratulation', 'success']
.some(kw => lower.includes(kw));
return { success, text };
},
this.baseUrl,
coin.urlPath,
csrfToken,
recaptchaToken,
coinSymbol
);
const { success, text } = result;
if (success) {
this.log(`βœ… Claim ${coinSymbol} berhasil!`);
// Ekstrak reward
const rewardPatterns = [
new RegExp(`(\\d+\\.?\\d*)\\s*${coinSymbol}`, 'i'),
/claimed\s*:\s*(\d+\.?\d*)/i,
/reward\s*:\s*(\d+\.?\d*)/i,
/you received.*?(\d+\.?\d*)/i,
/(\d+\.?\d*)\s*coins?/i
];
let rewardFound = 0;
for (const pattern of rewardPatterns) {
const match = text.match(pattern);
if (match) {
const reward = parseFloat(match[1]);
if (!isNaN(reward) && reward > 0) {
rewardFound = reward;
coin.totalRewards += reward;
this.log(`πŸ’° Reward: ${reward} ${coinSymbol}`);
break;
}
}
}
if (rewardFound === 0) {
const defaultRewards = { LTC: 0.000001, DOGE: 0.00001, TRUMP: 0.01 };
const defaultReward = defaultRewards[coinSymbol] || 0.0001;
coin.totalRewards += defaultReward;
this.log(`πŸ’° Reward: ~${defaultReward} ${coinSymbol} (estimasi)`);
}
coin.successCount++;
this.consecutiveFailures = 0;
} else {
this.log(`❌ Claim ${coinSymbol} gagal!`);
this.consecutiveFailures++;
// Cek pesan error
const errorKeywords = ['error', 'failed', 'try again', 'cooldown', 'already claimed', 'wait', 'invalid'];
for (const kw of errorKeywords) {
if (text.toLowerCase().includes(kw)) {
this.log(`⚠️ Reason: ${kw}`);
break;
}
}
}
// Update total claims & last claim
coin.totalClaims++;
coin.lastClaim = Date.now() / 1000;
// Ekstrak cooldown
const extractedCooldown = this.extractCooldownFromResponse(text);
if (extractedCooldown && extractedCooldown > 0) {
coin.cooldown = extractedCooldown;
this.log(`⏰ Cooldown detected: ${extractedCooldown} detik`);
} else {
coin.cooldown = coin.defaultCooldown;
this.log(`⏰ Default cooldown: ${coin.defaultCooldown} detik`);
}
await this.saveStats();
return { success, response: text };
} catch (e) {
this.log(`❌ Error saat claim ${coinSymbol}: ${e.message}`);
this.consecutiveFailures++;
return { success: false };
}
}
// ========== GET NEXT AVAILABLE COIN ==========
getNextAvailableCoin() {
const currentTime = Date.now() / 1000;
for (const [symbol, coin] of Object.entries(this.COINS)) {
const timeSinceLast = currentTime - coin.lastClaim;
if (coin.lastClaim === 0 || timeSinceLast >= coin.cooldown) {
return { coin: symbol, waitTime: 0 };
}
}
let minWaitTime = Infinity;
let nextCoin = null;
for (const [symbol, coin] of Object.entries(this.COINS)) {
const timeSinceLast = currentTime - coin.lastClaim;
const waitTime = coin.cooldown - timeSinceLast;
if (waitTime < minWaitTime) {
minWaitTime = waitTime;
nextCoin = symbol;
}
}
return { coin: nextCoin, waitTime: minWaitTime > 0 ? minWaitTime : 0 };
}
// ========== RUN CYCLE ==========
async runCycle() {
this.totalCycles++;
this.log(`\nπŸ”„ CYCLE #${this.totalCycles}`);
const { coin: nextCoin, waitTime } = this.getNextAvailableCoin();
if (waitTime > 0) {
this.log(`⏳ Semua coin cooldown, tunggu ${Math.ceil(waitTime)} detik untuk ${nextCoin}...`);
await this.sleep(waitTime * 1000);
}
const result = await this.claimCoin(nextCoin);
if (!result.success) {
if (this.consecutiveFailures >= this.maxConsecutiveFailures) {
this.log(`⚠️ Terlalu banyak gagal (${this.consecutiveFailures}), istirahat 5 menit...`);
await this.sleep(300000);
this.consecutiveFailures = 0;
}
}
await this.saveStats();
await this.sleep(2000);
return result.success;
}
// ========== START ==========
async start() {
if (this.isRunning) {
this.log('[bot] ⚠️ Sudah berjalan');
return;
}
this.isRunning = true;
this.log('\nπŸš€ ===== STARTING BOT =====');
// Launch browser
try {
this.browser = await chromium.launch({
headless: true,
args: ['--no-sandbox', '--disable-setuid-sandbox']
});
const context = await this.browser.newContext({
viewport: { width: 1280, height: 720 },
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
});
await this.loadCookies(context);
this.page = await context.newPage();
} catch (e) {
this.log(`[browser] ❌ Gagal launch: ${e.message}`);
this.isRunning = false;
return;
}
// Login
if (!await this.login()) {
this.log('[bot] ❌ Login gagal, hentikan bot');
await this.browser.close();
this.isRunning = false;
return;
}
// Main loop
try {
while (this.isRunning && this.totalCycles < CONFIG.maxCycles) {
if (this.consecutiveFailures >= this.maxConsecutiveFailures) {
this.log('⚠️ Terlalu banyak gagal, pause 5 menit...');
await this.sleep(300000);
this.consecutiveFailures = 0;
}
await this.runCycle();
}
if (this.totalCycles >= CONFIG.maxCycles) {
this.log('🏁 Maksimum cycle tercapai');
}
} catch (e) {
this.log(`[bot] ❌ Error loop: ${e.message}`);
} finally {
await this.browser.close();
this.isRunning = false;
}
}
// ========== STOP ==========
async stop() {
this.log('[bot] πŸ›‘ Menghentikan bot...');
this.isRunning = false;
if (this.browser) await this.browser.close();
}
sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
}
// ========== ENTRY POINT ==========
if (import.meta.url === `file://${process.argv[1]}`) {
const bot = new MultiCoinFaucetBot();
bot.start().catch(console.error);
}
export { MultiCoinFaucetBot };