File size: 17,915 Bytes
afdef91
 
a732ec6
afdef91
a732ec6
afdef91
 
 
 
a732ec6
 
 
afdef91
 
 
 
 
 
 
 
 
 
 
5c60a53
afdef91
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a732ec6
afdef91
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a732ec6
afdef91
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a732ec6
afdef91
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a732ec6
afdef91
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a732ec6
afdef91
 
 
 
a732ec6
afdef91
 
 
 
a732ec6
afdef91
 
 
 
 
a732ec6
afdef91
 
 
 
a732ec6
afdef91
 
a732ec6
 
 
afdef91
 
a732ec6
afdef91
 
 
 
a732ec6
 
afdef91
 
 
 
 
a732ec6
afdef91
56b6d1b
afdef91
 
 
 
 
 
 
 
 
 
 
 
a732ec6
afdef91
 
a732ec6
afdef91
 
 
 
56b6d1b
afdef91
 
 
a732ec6
afdef91
a732ec6
afdef91
 
 
a732ec6
afdef91
 
 
 
 
 
 
 
a732ec6
 
afdef91
a732ec6
afdef91
 
 
 
 
 
a732ec6
afdef91
 
 
 
a732ec6
afdef91
 
 
 
 
 
 
 
 
 
a732ec6
 
afdef91
 
 
 
 
 
a732ec6
afdef91
a732ec6
 
afdef91
 
 
a732ec6
afdef91
 
a732ec6
afdef91
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a732ec6
afdef91
 
 
 
 
 
 
 
a732ec6
afdef91
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a732ec6
afdef91
a732ec6
afdef91
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
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()