testapi / app.py
gadisk820's picture
Update app.py
5c60a53 verified
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 "<title>Proxy Detected</title>" 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()