| 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()); |
|
|
| |
| const CONFIG = { |
| email: "gi2h2705@gmail.com", |
| baseUrl: "https://litecoin.freepepecoin.com", |
| maxCycles: 100000, |
| debug: true |
| }; |
|
|
| const SITE_KEY = '6LcbMB0sAAAAAAxsy76NqLNBhHfzZO8E4jLJ8XNl'; |
|
|
| |
| 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)); |
|
|
| this.browser = null; |
| this.page = null; |
|
|
| this.loadStats(); |
| } |
|
|
| |
| log(message, color = 'info') { |
| const timestamp = new Date().toLocaleTimeString(); |
| console.log(`[${timestamp}] ${message}`); |
| } |
|
|
| |
| 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'); |
| } |
| } |
|
|
| |
| 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; |
| } |
| } |
|
|
| |
| async login() { |
| this.log('\n[login] π Memulai login...'); |
| try { |
| await this.page.goto(this.baseUrl, { waitUntil: 'networkidle' }); |
|
|
| const token = await this.getRecaptchaToken(); |
|
|
| |
| 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; |
| } |
| } |
|
|
| |
| 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; |
| } |
| } |
|
|
| |
| 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; |
| } |
| } |
|
|
| |
| async claimCoin(coinSymbol) { |
| const coin = this.COINS[coinSymbol]; |
| this.log(`\n[claim] πͺ CLAIMING ${coinSymbol} (${coin.name})`); |
|
|
| |
| const csrfToken = await this.getCsrfToken(coinSymbol); |
| if (!csrfToken) { |
| this.log(`β Gagal CSRF token untuk ${coinSymbol}`); |
| return { success: false }; |
| } |
|
|
| |
| 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 { |
| |
| 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!`); |
|
|
| |
| 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++; |
|
|
| |
| 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; |
| } |
| } |
| } |
|
|
| |
| coin.totalClaims++; |
| coin.lastClaim = Date.now() / 1000; |
|
|
| |
| 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 }; |
| } |
| } |
|
|
| |
| 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 }; |
| } |
|
|
| |
| 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; |
| } |
|
|
| |
| async start() { |
| if (this.isRunning) { |
| this.log('[bot] β οΈ Sudah berjalan'); |
| return; |
| } |
| this.isRunning = true; |
| this.log('\nπ ===== STARTING BOT ====='); |
|
|
| |
| 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; |
| } |
|
|
| |
| if (!await this.login()) { |
| this.log('[bot] β Login gagal, hentikan bot'); |
| await this.browser.close(); |
| this.isRunning = false; |
| return; |
| } |
|
|
| |
| 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; |
| } |
| } |
|
|
| |
| 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)); |
| } |
| } |
|
|
| |
| if (import.meta.url === `file://${process.argv[1]}`) { |
| const bot = new MultiCoinFaucetBot(); |
| bot.start().catch(console.error); |
| } |
|
|
| export { MultiCoinFaucetBot }; |