import os import random import time import threading import re import cloudscraper import requests from bs4 import BeautifulSoup from colorama import init, Fore init(autoreset=True) # ================= KONFIGURASI ================= BASE_URL = "https://firefaucet.win" DASHBOARD_URL = f"{BASE_URL}/" FAUCET_URL = f"{BASE_URL}/faucet/" PAYOUT_URL = f"{BASE_URL}/internal-api/payout/" BALANCE_URL = f"{BASE_URL}/balance/" WITHDRAW_INFO_URL = f"{BASE_URL}/internal-api/withdraw/get-withdrawal-info/" WITHDRAW_URL = f"{BASE_URL}/api/withdraw/" # <--- PERBAIKAN: endpoint withdraw LEVELS_URL = f"{BASE_URL}/levels" # Environment variables SOLVER_URLS = os.getenv("SOLVER_URLS", "https://gadisk820-testapi.hf.space").split(',') SOLVER_KEY = os.getenv("SOLVER_KEY", "00000000000000000000#0000000000000000000#000000000000000000#") PROXY_URL = os.getenv("PROXY_URL", None) LTC_ADDRESS = os.getenv("LTC_ADDRESS", "ltc1qrthepxg7yxs5d8v94k6y8p74q0qxvv7vk5qe7m") # Alamat LTC tujuan withdraw HEADERS = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", "Referer": DASHBOARD_URL } COOKIES = { "session": "ae530762-a411-49c1-9b86-00741a937b60.l40aeEK2wwSTMN2ifOUFUIi7KVw", "refer_by" : "1022644" } # ================= SOLVER TURNSTILE ================= class TurnstileSolver: def __init__(self, email=""): self.email = email self.solver_url = random.choice(SOLVER_URLS).strip() self.sitekey = "0x4AAAAAAAEUvFih09RuyAna" def solve(self, domain): domain_with_proto = f"https://{domain}" if not domain.startswith('http') else domain headers = {"Content-Type": "application/json", "key": SOLVER_KEY} data = {"type": "turnstile", "domain": domain_with_proto, "siteKey": self.sitekey} for attempt in range(1, 6): try: resp = requests.post(f"{self.solver_url}/solve", headers=headers, json=data, timeout=30) result = resp.json() if "taskId" not in result: print(Fore.RED + f"❌ Tidak ada taskId (percobaan {attempt})", flush=True) continue task_id = result["taskId"] for _ in range(30): time.sleep(5) poll = requests.post(f"{self.solver_url}/solve", headers=headers, json={"taskId": task_id}, timeout=30) poll_res = poll.json() if poll_res.get("status") == "done": token = poll_res.get("token") or poll_res.get("solution", {}).get("token") if token: print(Fore.GREEN + "✅ Token Turnstile diterima", flush=True) return token elif poll_res.get("status") == "error": print(Fore.RED + f"⚠️ Error solver: {poll_res}", flush=True) break print(Fore.YELLOW + "⏳ Timeout polling, coba solver lain?", flush=True) except Exception as e: print(Fore.RED + f"⚠️ Solver error: {e}", flush=True) time.sleep(2 ** attempt) return None # ================= BOT UTAMA ================= class FaucetBot: def __init__(self, cookies, email="user@example.com"): self.session = cloudscraper.create_scraper() self.session.cookies.update(cookies) self.email = email self.solver = TurnstileSolver(email) self.running = True self.proxy_url = PROXY_URL self.using_proxy = False if self.proxy_url: self.proxy_session = cloudscraper.create_scraper() self.proxy_session.cookies.update(cookies) proxies = {"http": self.proxy_url, "https": self.proxy_url} self.proxy_session.proxies.update(proxies) else: self.proxy_session = None def get_csrf_token(self, use_proxy=False): session = self.proxy_session if use_proxy and self.proxy_session else self.session try: resp = session.get(DASHBOARD_URL, headers=HEADERS, timeout=30) resp.raise_for_status() soup = BeautifulSoup(resp.text, "html.parser") token_input = soup.find("input", {"name": "csrf_token"}) if token_input: return token_input['value'] else: print(Fore.RED + "⚠️ CSRF token tidak ditemukan", flush=True) return None except Exception as e: print(Fore.RED + f"⚠️ Gagal ambil CSRF: {e}", flush=True) return None def check_cooldown(self, html): match = re.search(r'(?:after|in)\s+(\d+)\s*(minute|minutes|hour|hours)', html, re.IGNORECASE) if match: value = int(match.group(1)) unit = match.group(2).lower() seconds = value * 3600 if 'hour' in unit else value * 60 print(Fore.YELLOW + f"⏳ Cooldown {value} {unit}, menunggu {seconds} detik...", flush=True) time.sleep(seconds) return True if re.search(r'please come back after|try again later|cooldown', html, re.IGNORECASE): default_wait = 30 * 60 print(Fore.YELLOW + f"⏳ Cooldown terdeteksi (tanpa durasi), menunggu {default_wait//60} menit default...", flush=True) time.sleep(default_wait) return True return False def get_balance(self, use_proxy=False): session = self.proxy_session if use_proxy and self.proxy_session else self.session try: resp = session.get(BALANCE_URL, headers=HEADERS, timeout=30) if resp.status_code != 200: return None soup = BeautifulSoup(resp.text, "html.parser") usd_elem = soup.find("span", id="ltc-usd-balance") ltc_elem = soup.find("b", id="ltc-balance") usd = usd_elem.text.strip() if usd_elem else "N/A" ltc = ltc_elem.text.strip() if ltc_elem else "N/A" return usd, ltc except Exception as e: print(Fore.RED + f"⚠️ Gagal ambil balance: {e}", flush=True) return None # ========== FUNGSI WITHDRAW ========== def get_withdrawal_info(self, coin="ltc", use_proxy=False): session = self.proxy_session if use_proxy and self.proxy_session else self.session params = {"coin": coin} try: resp = session.get(WITHDRAW_INFO_URL, headers=HEADERS, params=params, timeout=30) resp.raise_for_status() return resp.json() except Exception as e: print(Fore.RED + f"⚠️ Gagal ambil info withdrawal: {e}", flush=True) return None def withdraw(self, coin="ltc", address=None, amount=None, use_proxy=False): """Lakukan withdraw dengan amount USD ke address tertentu (network mainnet)""" if not address: address = LTC_ADDRESS if not address: print(Fore.RED + "❌ Alamat LTC tidak ditemukan. Set LTC_ADDRESS di environment variable.", flush=True) return False # Ambil info withdrawal info = self.get_withdrawal_info(coin, use_proxy=use_proxy) if not info: return False user_balance_usd = info['user']['balance_usd'] if user_balance_usd < 4.0: print(Fore.YELLOW + f"⏳ Saldo belum mencukupi untuk withdraw: ${user_balance_usd:.2f} < $4", flush=True) return False daily_limit = info['user']['daily_withdrawal_limit'] max_possible = min(user_balance_usd, daily_limit['max'] - daily_limit['used']) if max_possible < 4.0: print(Fore.YELLOW + f"⏳ Maksimum withdraw ${max_possible:.2f} < $4", flush=True) return False if amount is None: amount = max_possible else: amount = min(amount, max_possible) # Dapatkan CSRF token csrf = self.get_csrf_token(use_proxy=use_proxy) if not csrf: return False # Data POST sesuai form (network mainnet untuk LTC) data = { "csrf_token": csrf, "coin": coin, "network": "mainnet", # <--- PENTING: field network "address": address, "amount": str(amount) } headers_post = HEADERS.copy() headers_post.update({ "Content-Type": "application/x-www-form-urlencoded", "X-Requested-With": "XMLHttpRequest" }) session = self.proxy_session if use_proxy and self.proxy_session else self.session try: resp = session.post(WITHDRAW_URL, headers=headers_post, data=data, timeout=30) if resp.status_code == 200: result = resp.json() if result.get('success'): print(Fore.GREEN + f"✅ Withdraw sukses! ID: {result.get('withdrawal_id')}", flush=True) return True else: print(Fore.RED + f"❌ Withdraw gagal: {result.get('message')}", flush=True) return False else: print(Fore.RED + f"❌ Withdraw error status: {resp.status_code}", flush=True) return False except Exception as e: print(Fore.RED + f"⚠️ Gagal POST withdraw: {e}", flush=True) return False # ========== FUNGSI LEVEL ========== def check_and_claim_levels(self, use_proxy=False): """Cek level tiap jam dan klaim jika tersedia""" session = self.proxy_session if use_proxy and self.proxy_session else self.session try: resp = session.get(LEVELS_URL, headers=HEADERS) resp.raise_for_status() soup = BeautifulSoup(resp.text, "html.parser") collect_buttons = [a['href'] for a in soup.select("a.collect-btn") if 'disabled' not in a.get('class', [])] if not collect_buttons: print(Fore.YELLOW + "⚠️ Tidak ada level yang tersedia untuk diklaim saat ini.") return for href in collect_buttons: url = href if href.startswith("http") else BASE_URL + href print(Fore.MAGENTA + f"🟢 Mengklaim level: {url}") claim_resp = session.get(url, headers=HEADERS) if claim_resp.status_code == 200: print(Fore.GREEN + "✅ Claim level berhasil!") else: print(Fore.RED + f"⚠️ Claim gagal, status code: {claim_resp.status_code}") except Exception as e: print(Fore.RED + "⚠️ Error saat cek atau klaim level:", e) # ========== LOOP UTAMA ========== def claim_faucet(self, use_proxy=False): session = self.proxy_session if use_proxy and self.proxy_session else self.session csrf = self.get_csrf_token(use_proxy=use_proxy) if not csrf: return False token = self.solver.solve("firefaucet.win") if not token: print(Fore.RED + "❌ Gagal mendapatkan token Turnstile", flush=True) return False data = { "csrf_token": csrf, "selected-captcha": "turnstile", "cf-turnstile-response": token } headers_post = HEADERS.copy() headers_post.update({ "Content-Type": "application/x-www-form-urlencoded", "X-Requested-With": "XMLHttpRequest" }) try: resp = session.post(FAUCET_URL, headers=headers_post, data=data, timeout=30, allow_redirects=True) html = resp.text print(Fore.CYAN + f"📍 Response URL: {resp.url}", flush=True) print(Fore.CYAN + f"📊 Status code: {resp.status_code}", flush=True) if resp.history: print(Fore.YELLOW + "🔄 Redirect history:", [r.url for r in resp.history], flush=True) except Exception as e: print(Fore.RED + f"⚠️ Gagal POST claim: {e}", flush=True) return False if "Proxy Detected" in html or "Proxy Detected" in html: print(Fore.RED + "🚫 Proxy terdeteksi!", flush=True) return "proxy_detected" if self.check_cooldown(html): return False success = False soup = BeautifulSoup(html, "html.parser") success_div = soup.find("div", class_="success_msg") if success_div: print(Fore.GREEN + f"✅ Claim sukses: {success_div.text.strip()}", flush=True) success = True elif "successfully added" in html.lower(): print(Fore.GREEN + "✅ Claim sukses (dari teks)", flush=True) success = True else: print(Fore.RED + "❌ Claim gagal, respon tidak dikenali", flush=True) print(Fore.YELLOW + "📄 HTML preview (first 500 chars):", flush=True) print(html[:500], flush=True) return success def payout_loop(self): while self.running: try: resp = self.session.get(PAYOUT_URL, headers=HEADERS, timeout=30) resp.raise_for_status() data = resp.json() if data.get("success"): print(Fore.GREEN + f"💰 Payout berhasil! Balance: {data.get('balance')}", flush=True) print(Fore.CYAN + f"⏳ Next payout: {data.get('time_left')}", flush=True) else: print(Fore.RED + f"⚠️ Payout gagal: {data}", flush=True) except Exception as e: print(Fore.RED + f"⚠️ Gagal request payout: {e}", flush=True) for _ in range(60): if not self.running: break time.sleep(1) def balance_loop(self): while self.running: balance = self.get_balance(use_proxy=self.using_proxy) if balance: usd, ltc = balance print(Fore.CYAN + f"💵 Balance: ${usd} USD | {ltc} LTC", flush=True) else: print(Fore.RED + "❌ Gagal mengambil balance", flush=True) for _ in range(300): if not self.running: break time.sleep(1) def withdraw_loop(self): """Loop pengecekan saldo untuk withdraw setiap 5 menit""" while self.running: time.sleep(300) # 5 menit info = self.get_withdrawal_info(use_proxy=self.using_proxy) if info and info['user']['balance_usd'] >= 4.0: print(Fore.CYAN + f"💰 Saldo mencukupi untuk withdraw: ${info['user']['balance_usd']:.2f}", flush=True) self.withdraw(use_proxy=self.using_proxy) else: if info: print(Fore.CYAN + f"💰 Saldo saat ini: ${info['user']['balance_usd']:.2f} (< $4)", flush=True) def level_loop(self): """Loop pengecekan level setiap jam""" while self.running: self.check_and_claim_levels(use_proxy=self.using_proxy) # Tunggu 1 jam (3600 detik) sebelum cek lagi for _ in range(3600): if not self.running: break time.sleep(1) def claim_loop(self): while self.running: result = self.claim_faucet(use_proxy=False) if result == "proxy_detected": if self.proxy_session and not self.using_proxy: print(Fore.YELLOW + "🔄 Mencoba menggunakan proxy...", flush=True) result2 = self.claim_faucet(use_proxy=True) if result2 == "proxy_detected": print(Fore.RED + "🚫 Proxy juga terdeteksi. Menunggu 1 jam sebelum coba lagi...", flush=True) time.sleep(3600) elif result2 is True: print(Fore.GREEN + "✅ Claim sukses dengan proxy!", flush=True) self.using_proxy = True time.sleep(60) continue else: print(Fore.RED + "❌ Claim dengan proxy gagal karena alasan lain.", flush=True) time.sleep(10) else: print(Fore.RED + "🚫 Proxy terdeteksi dan tidak ada proxy alternatif. Menunggu 1 jam...", flush=True) time.sleep(3600) elif result is True: print(Fore.CYAN + "⏳ Menunggu 60 detik sebelum claim berikutnya...", flush=True) time.sleep(60) else: print(Fore.CYAN + "⏳ Mencoba lagi dalam 10 detik...", flush=True) time.sleep(10) def run(self): print(Fore.MAGENTA + "🔹 Memulai bot faucet dengan Turnstile solver, thread payout, balance monitor, withdraw monitor, dan level checker...", flush=True) payout_thread = threading.Thread(target=self.payout_loop, daemon=True) payout_thread.start() balance_thread = threading.Thread(target=self.balance_loop, daemon=True) balance_thread.start() withdraw_thread = threading.Thread(target=self.withdraw_loop, daemon=True) withdraw_thread.start() level_thread = threading.Thread(target=self.level_loop, daemon=True) level_thread.start() try: self.claim_loop() except KeyboardInterrupt: print(Fore.YELLOW + "\n🛑 Dihentikan oleh user.", flush=True) self.running = False # ================= EKSEKUSI ================= if __name__ == "__main__": bot = FaucetBot(COOKIES, email="your_email@example.com") bot.run()