gi2h270 commited on
Commit
c0d6bcb
Β·
verified Β·
1 Parent(s): 9b168e1

Upload 19 files

Browse files
.dockerignore ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ __pycache__/
2
+ *.pyc
3
+ *.pyo
4
+ *.pyd
5
+ .Python
6
+ env/
7
+ venv/
8
+ .venv/
9
+ env.bak/
10
+ venv.bak/
11
+ *.log
12
+ logs/
13
+ data/
14
+ *.db
15
+ .DS_Store
16
+ .git/
17
+ .gitignore
18
+ .vscode/
19
+ .idea/
20
+ *.md
21
+ docker-compose.override.yml
.env ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Email configuration
2
+ EMAIL=gi2h27@gmail.com
3
+
4
+ # Faucet configuration
5
+ MAX_CLAIMS=100000
6
+ COOLDOWN_OVERRIDE=5
7
+
8
+ # Proxy settings (jika perlu)
9
+ HTTP_PROXY=
10
+ HTTPS_PROXY=
11
+ NO_PROXY=localhost,127.0.0.1
12
+
13
+ # Solver settings
14
+ SOLVER_URL=https://gi2h-xxx.hf.space
15
+ SOLVER_KEY=00000000000000000000#0000000000000000000#000000000000000000#
Api/.gitattributes ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
Api/Api.js ADDED
@@ -0,0 +1,205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * API.js
3
+ */
4
+ const express = require('express');
5
+ const { connect } = require("puppeteer-real-browser");
6
+ const fs = require('fs');
7
+ const path = require('path');
8
+
9
+ const app = express();
10
+ const port = process.env.PORT || 7860;
11
+ const authToken = process.env.authToken || null;
12
+
13
+ global.browserLimit = 100;
14
+ global.timeOut = 300000;
15
+
16
+ // cache
17
+ const CACHE_DIR = path.join(__dirname, "cache");
18
+ const CACHE_FILE = path.join(CACHE_DIR, "cache.json");
19
+ const CACHE_TTL = 5 * 60 * 1000;
20
+ const CACHE_AUTOSAVE = process.env.CACHE_AUTOSAVE === "true";
21
+
22
+ function readCache(type, taskId) {
23
+ const file = path.join(CACHE_DIR, type, `${taskId}.json`);
24
+ if (!fs.existsSync(file)) return null;
25
+
26
+ try {
27
+ const data = JSON.parse(fs.readFileSync(file, 'utf-8'));
28
+ console.log(`cache check: ${type}:${taskId} => ${data ? "HIT" : "MISS"}`);
29
+ if (Date.now() - data.timestamp < CACHE_TTL) {
30
+ return data;
31
+ }
32
+ return null;
33
+ } catch {
34
+ return null;
35
+ }
36
+ }
37
+
38
+ function writeCache(type, taskId, value) {
39
+ const dir = path.join(CACHE_DIR, type);
40
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
41
+
42
+ const file = path.join(dir, `${taskId}.json`);
43
+ const data = { timestamp: Date.now(), ...value };
44
+
45
+ fs.writeFileSync(file, JSON.stringify(data, null, 2), 'utf-8');
46
+ console.log(`cache saved: ${type}:${taskId}`);
47
+ }
48
+
49
+ function cleanCache() {
50
+ const types = ["turnstile", "recaptcha3", "recaptcha2", "interstitial", "error"];
51
+ const now = Date.now();
52
+ const TTL = 60 * 60 * 1000;
53
+
54
+ types.forEach(type => {
55
+ const dir = path.join(CACHE_DIR, type);
56
+ if (!fs.existsSync(dir)) return;
57
+
58
+ fs.readdirSync(dir).forEach(file => {
59
+ const filePath = path.join(dir, file);
60
+ try {
61
+ const data = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
62
+ if (now - data.timestamp > TTL) {
63
+ fs.unlinkSync(filePath);
64
+ console.log(`cache expired: ${filePath}`);
65
+ }
66
+ } catch {
67
+ fs.unlinkSync(filePath);
68
+ }
69
+ });
70
+ });
71
+ }
72
+
73
+ setInterval(cleanCache, 600 * 1000);
74
+
75
+ app.use(express.json());
76
+ app.use(express.urlencoded({ extended: true }));
77
+ const tasks = {};
78
+
79
+ // Api_route
80
+ app.get("/", (req, res) => {
81
+ const baseUrl = `${req.protocol}://${req.get('host')}`;
82
+ const uptime = process.uptime();
83
+ res.json({
84
+ message: "Welcome",
85
+ server: {
86
+ domain: baseUrl,
87
+ version: "7.3.0",
88
+ uptime: `${Math.floor(uptime)} seconds`,
89
+ limit: global.browserLimit,
90
+ timeout: global.timeOut,
91
+ status: "recently running"
92
+ },
93
+ solvers: ["turnstile", "recaptcha2", "recaptcha3", "interstitial"]
94
+ });
95
+ });
96
+
97
+
98
+ app.post('/solve', async (req, res) => {
99
+ const { type, domain, siteKey, taskId, action, proxy, isInvisible } = req.body;
100
+
101
+ if (taskId) {
102
+ const task = tasks[taskId];
103
+ if (!task) return res.status(404).json({ status: "error", message: "Task not found" });
104
+
105
+ if (task.status === "pending") {
106
+ return res.json({ status: "processing" });
107
+ }
108
+
109
+ return res.json(task);
110
+ }
111
+
112
+ const newTaskId = Date.now().toString(36);
113
+ tasks[newTaskId] = { status: "pending" };
114
+ console.log(`New : ${newTaskId}=${type}:${domain}`);
115
+
116
+ (async () => {
117
+ try {
118
+ const ctx = await init_browser(proxy?.server);
119
+ const page = ctx.page;
120
+ let result;
121
+
122
+ switch (type) {
123
+ case "turnstile":
124
+ result = await turnstile({ domain, siteKey, action, proxy }, page);
125
+ tasks[newTaskId] = { status: "done", ...result };
126
+ console.log(`done: ${newTaskId}=${type}:${domain}`);
127
+ if (CACHE_AUTOSAVE) writeCache("turnstile", newTaskId, tasks[newTaskId]);
128
+ break;
129
+
130
+ case "interstitial":
131
+ result = await interstitial({ domain, proxy }, page);
132
+ tasks[newTaskId] = { status: "done", ...result };
133
+ console.log(`done: ${newTaskId}=${type}:${domain}`);
134
+ if (CACHE_AUTOSAVE) writeCache("interstitial", newTaskId, tasks[newTaskId]);
135
+ break;
136
+
137
+ case "recaptcha2":
138
+ result = await recaptchaV2({ domain, siteKey, action, isInvisible, proxy }, page);
139
+ tasks[newTaskId] = { status: "done", ...result };
140
+ console.log(`done: ${newTaskId}=${type}:${domain}`);
141
+ if (CACHE_AUTOSAVE) writeCache("recaptcha2", newTaskId, tasks[newTaskId]);
142
+ break;
143
+
144
+ case "recaptcha3":
145
+ result = await recaptchaV3({ domain, siteKey, action, proxy }, page);
146
+ tasks[newTaskId] = { status: "done", ...result };
147
+ console.log(`done: ${newTaskId}=${type}:${domain}`);
148
+ if (CACHE_AUTOSAVE) writeCache("recaptcha3", newTaskId, tasks[newTaskId]);
149
+ break;
150
+
151
+ default:
152
+ tasks[newTaskId] = { status: "error", message: "Invalid type" };
153
+ }
154
+
155
+ await ctx.browser.close();
156
+ console.log(`Browser closed ${newTaskId}`);
157
+ } catch (err) {
158
+ tasks[newTaskId] = { status: "error", message: "totally failed" };
159
+ console.error(`failed: ${newTaskId}=${type}:${domain}`);
160
+ console.error("Detailed error:", err);
161
+ }
162
+ })();
163
+
164
+ res.json({ taskId: newTaskId, status: "pending" });
165
+ });
166
+
167
+ // init_browser
168
+ async function init_browser(proxyServer = null) {
169
+ const connectOptions = {
170
+ headless: false,
171
+ turnstile: true,
172
+ connectOption: { defaultViewport: null },
173
+ disableXvfb: false,
174
+ };
175
+ if (proxyServer) connectOptions.args = [`--proxy-server=${proxyServer}`];
176
+
177
+ const { browser } = await connect(connectOptions);
178
+ const [page] = await browser.pages();
179
+
180
+ await page.goto('about:blank');
181
+ await page.setRequestInterception(true);
182
+ page.on('request', (req) => {
183
+ const type = req.resourceType();
184
+ if (["image", "stylesheet", "font", "media"].includes(type)) req.abort();
185
+ else req.continue();
186
+ });
187
+ console.log(`initialized${proxyServer ? "proxy= " + proxyServer : ""}`);
188
+
189
+ return { browser, page };
190
+ }
191
+
192
+
193
+ const turnstile = require('./Api/turnstile');
194
+ const interstitial = require('./Api/interstitial');
195
+ const recaptchaV2 = require('./Api/recaptcha2');
196
+ const recaptchaV3 = require('./Api/recaptcha3');
197
+
198
+ app.use((req, res) => {
199
+ res.status(404).json({ message: 'Not Found' });
200
+ console.warn(`error: ${req.method} ${req.originalUrl}`);
201
+ });
202
+
203
+ app.listen(port, () => {
204
+ console.log(`Server running: http://localhost:${port}`);
205
+ });
Api/Api/Api.md ADDED
@@ -0,0 +1 @@
 
 
1
+ Api
Api/Api/interstitial.js ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * INTERSTITIAL
3
+ */
4
+ async function interstitial({ domain, proxy }, page) {
5
+ return new Promise(async (resolve, reject) => {
6
+ if (!domain) return reject(new Error("Missing domain parameter"));
7
+
8
+ const startTime = Date.now();
9
+ let isResolved = false;
10
+ let userAgent = null;
11
+
12
+ const cl = setTimeout(() => {
13
+ if (!isResolved) {
14
+ isResolved = true;
15
+ resolve({
16
+ cf_clearance: null,
17
+ cookies: null,
18
+ user_agent: userAgent,
19
+ type: 'interstitial'
20
+ });
21
+ }
22
+ }, 20000);
23
+
24
+ try {
25
+ if (proxy?.username && proxy?.password) {
26
+ await page.authenticate({
27
+ username: proxy.username,
28
+ password: proxy.password,
29
+ });
30
+ }
31
+
32
+ page.removeAllListeners("request");
33
+ page.removeAllListeners("response");
34
+ await page.setRequestInterception(true);
35
+
36
+ page.on("request", async (req) => {
37
+ try {
38
+ await req.continue();
39
+ } catch (_) {}
40
+ });
41
+
42
+ page.on("response", async (res) => {
43
+ try {
44
+ const url = res.url();
45
+ if (url.includes("/cdn-cgi/challenge-platform/")) {
46
+ const headers = res.headers();
47
+ if (headers["set-cookie"]) {
48
+ const cookies = headers["set-cookie"];
49
+ const match = cookies.match(/cf_clearance=([^;]+)/);
50
+ if (match) {
51
+ const cf_clearance = match[1];
52
+ const userAgent = (await res.request().headers())["user-agent"];
53
+
54
+ if (!isResolved) {
55
+ isResolved = true;
56
+ clearTimeout(cl);
57
+ resolve({
58
+ cf_clearance,
59
+ cookies, // seluruh cookie string
60
+ user_agent: userAgent,
61
+ type: 'interstitial'
62
+ });
63
+ }
64
+ }
65
+ }
66
+ }
67
+ } catch (_) {}
68
+ });
69
+
70
+ await page.goto(domain, { waitUntil: "domcontentloaded" });
71
+ userAgent = await page.evaluate(() => navigator.userAgent);
72
+ } catch (err) {
73
+ if (!isResolved) {
74
+ isResolved = true;
75
+ clearTimeout(cl);
76
+ reject(err);
77
+ }
78
+ }
79
+ });
80
+ }
81
+
82
+ module.exports = interstitial;
Api/Api/recaptcha2.js ADDED
@@ -0,0 +1,228 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * API_RECAPTCHA2
3
+ */
4
+ async function recaptchaV2({ domain, siteKey, action = "submit", isInvisible = false, proxy }, page) {
5
+ if (!domain) throw new Error("Missing domain parameter");
6
+ if (!siteKey) throw new Error("Missing siteKey parameter");
7
+
8
+ const timeout = global.timeOut;
9
+
10
+ return new Promise(async (resolve, reject) => {
11
+ let isResolved = false;
12
+
13
+ const cl = setTimeout(() => {
14
+ if (!isResolved) {
15
+ isResolved = true;
16
+ reject(new Error("Timeout Error"));
17
+ }
18
+ }, timeout);
19
+
20
+ try {
21
+ if (proxy?.username && proxy?.password) {
22
+ await page.authenticate({
23
+ username: proxy.username,
24
+ password: proxy.password,
25
+ });
26
+ }
27
+
28
+ const htmlContent = `
29
+ <!DOCTYPE html>
30
+ <html lang="en">
31
+ <head>
32
+ <meta charset="UTF-8">
33
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
34
+ <title>reCAPTCHA v2 Solver</title>
35
+ <style>
36
+ body {
37
+ font-family: Arial, sans-serif;
38
+ margin: 0;
39
+ padding: 20px;
40
+ background: #f5f5f5;
41
+ display: flex;
42
+ justify-content: center;
43
+ align-items: center;
44
+ min-height: 100vh;
45
+ }
46
+ .container {
47
+ background: white;
48
+ padding: 30px;
49
+ border-radius: 10px;
50
+ box-shadow: 0 2px 10px rgba(0,0,0,0.1);
51
+ text-align: center;
52
+ }
53
+ .status {
54
+ margin-top: 20px;
55
+ padding: 10px;
56
+ border-radius: 5px;
57
+ background: #f8f9fa;
58
+ }
59
+ button {
60
+ background: #007bff;
61
+ color: white;
62
+ border: none;
63
+ padding: 10px 20px;
64
+ border-radius: 5px;
65
+ cursor: pointer;
66
+ margin: 10px;
67
+ }
68
+ button:hover { background: #0056b3; }
69
+ </style>
70
+ </head>
71
+ <body>
72
+ <div class="container">
73
+ <h2>reCAPTCHA v2 Solver</h2>
74
+ <p>SiteKey: ${siteKey}</p>
75
+ <div id="recaptcha-container">
76
+ <div class="g-recaptcha"
77
+ data-sitekey="${siteKey}"
78
+ data-callback="recaptchaCallback"
79
+ data-expired-callback="recaptchaExpired"
80
+ data-error-callback="recaptchaError"
81
+ data-size="${isInvisible ? 'invisible' : 'normal'}"
82
+ data-theme="light"></div>
83
+ </div>
84
+ ${isInvisible ? '<button onclick="executeInvisible()">Execute reCAPTCHA</button>' : ''}
85
+ <button onclick="checkToken()">Check Token</button>
86
+ <div class="status" id="status">Waiting for reCAPTCHA...</div>
87
+ </div>
88
+
89
+ <script>
90
+ window.recaptchaToken = null;
91
+ window.recaptchaSolved = false;
92
+
93
+ window.recaptchaCallback = function(token) {
94
+ console.log('reCAPTCHA token received:', token);
95
+ window.recaptchaToken = token;
96
+ window.recaptchaSolved = true;
97
+
98
+ document.getElementById('status').innerHTML =
99
+ 'βœ… reCAPTCHA Solved! Token: ' + token.substring(0, 20) + '...';
100
+ document.getElementById('status').style.background = '#d4edda';
101
+ document.getElementById('status').style.color = '#155724';
102
+
103
+ var input = document.createElement('input');
104
+ input.type = 'hidden';
105
+ input.name = 'g-recaptcha-response';
106
+ input.value = token;
107
+ input.id = 'recaptcha-token-input';
108
+ document.body.appendChild(input);
109
+
110
+ localStorage.setItem('recaptcha_token', token);
111
+ };
112
+
113
+ window.recaptchaExpired = function() {
114
+ console.log('reCAPTCHA expired');
115
+ window.recaptchaToken = null;
116
+ window.recaptchaSolved = false;
117
+ document.getElementById('status').innerHTML = '❌ reCAPTCHA Expired - Refreshing...';
118
+ document.getElementById('status').style.background = '#fff3cd';
119
+ document.getElementById('status').style.color = '#856404';
120
+
121
+ var existing = document.getElementById('recaptcha-token-input');
122
+ if (existing) existing.remove();
123
+
124
+ setTimeout(() => { if (window.grecaptcha) grecaptcha.reset(); }, 1000);
125
+ };
126
+
127
+ window.recaptchaError = function() {
128
+ console.log('reCAPTCHA error');
129
+ document.getElementById('status').innerHTML = '❌ reCAPTCHA Error';
130
+ document.getElementById('status').style.background = '#f8d7da';
131
+ document.getElementById('status').style.color = '#721c24';
132
+ };
133
+
134
+ window.executeInvisible = function() {
135
+ if (window.grecaptcha) grecaptcha.execute();
136
+ };
137
+
138
+ window.checkToken = function() {
139
+ const token = window.recaptchaToken ||
140
+ document.getElementById('recaptcha-token-input')?.value;
141
+ document.getElementById('status').innerHTML = token
142
+ ? 'Token: ' + token
143
+ : 'No token yet';
144
+ };
145
+
146
+ window.onload = function() {
147
+ setTimeout(function() {
148
+ if (${isInvisible} && window.grecaptcha) {
149
+ grecaptcha.execute();
150
+ }
151
+ if (!${isInvisible}) {
152
+ var iframe = document.querySelector('iframe[src*="recaptcha"]');
153
+ if (iframe) {
154
+ console.log('Attempting to interact with reCAPTCHA');
155
+ var rect = iframe.getBoundingClientRect();
156
+ var clickEvent = new MouseEvent('click', {
157
+ view: window, bubbles: true, cancelable: true,
158
+ clientX: rect.left + rect.width / 2,
159
+ clientY: rect.top + rect.height / 2
160
+ });
161
+ iframe.dispatchEvent(clickEvent);
162
+ }
163
+ }
164
+ }, 2000);
165
+ };
166
+ </script>
167
+
168
+ <script src="https://www.google.com/recaptcha/api.js" async defer></script>
169
+ </body>
170
+ </html>
171
+ `;
172
+
173
+ await page.setRequestInterception(true);
174
+ page.removeAllListeners("request");
175
+ page.on("request", async (req) => {
176
+ const url = req.url();
177
+ if ([domain, domain + "/"].includes(url) && req.resourceType() === "document") {
178
+ await req.respond({ status: 200, contentType: "text/html", body: htmlContent });
179
+ } else if (["image", "stylesheet", "font"].includes(req.resourceType())) {
180
+ await req.abort();
181
+ } else {
182
+ await req.continue();
183
+ }
184
+ });
185
+
186
+ await page.goto(domain, { waitUntil: "domcontentloaded", timeout });
187
+ await page.waitForSelector('.g-recaptcha', { timeout: 10000 });
188
+
189
+ const tokenHandle = await page.waitForFunction(() => {
190
+ const input = document.querySelector('#recaptcha-token-input');
191
+ if (input?.value?.length > 10) return input.value;
192
+ const stored = localStorage.getItem('recaptcha_token');
193
+ if (stored?.length > 10) return stored;
194
+ if (window.recaptchaToken?.length > 10) return window.recaptchaToken;
195
+ return null;
196
+ }, { timeout, polling: 100 });
197
+
198
+ const tokenValue = await tokenHandle.jsonValue();
199
+
200
+ isResolved = true;
201
+ clearTimeout(cl);
202
+
203
+ if (!tokenValue || tokenValue.length < 10) {
204
+ reject(new Error("Failed to get valid reCAPTCHA token"));
205
+ } else {
206
+ resolve({ token: tokenValue, type: 'recaptcha_v2' });
207
+ }
208
+ } catch (error) {
209
+ if (!isResolved) {
210
+ isResolved = true;
211
+ clearTimeout(cl);
212
+ try {
213
+ const fallbackToken = await page.evaluate(() => {
214
+ const input = document.querySelector('#recaptcha-token-input');
215
+ return input ? input.value : null;
216
+ });
217
+ if (fallbackToken?.length > 10) {
218
+ resolve({ token: fallbackToken, type: 'recaptcha_v2' });
219
+ return;
220
+ }
221
+ } catch {}
222
+ reject(new Error(`reCAPTCHA solving failed: ${error.message}`));
223
+ }
224
+ }
225
+ });
226
+ }
227
+
228
+ module.exports = recaptchaV2;
Api/Api/recaptcha3.js ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * recaptcha3.js
3
+ */
4
+ async function recaptchaV3({ domain, siteKey, action, proxy }, page) {
5
+ if (!domain) throw new Error("Missing domain parameter");
6
+ if (!siteKey) throw new Error("Missing siteKey parameter");
7
+
8
+ const timeout = 120000;
9
+
10
+ return new Promise(async (resolve, reject) => {
11
+ let isResolved = false;
12
+
13
+ const cl = setTimeout(() => {
14
+ if (!isResolved) {
15
+ isResolved = true;
16
+ reject(new Error("Timeout Error"));
17
+ }
18
+ }, timeout);
19
+
20
+ try {
21
+ if (proxy?.username && proxy?.password) {
22
+ await page.authenticate({
23
+ username: proxy.username,
24
+ password: proxy.password,
25
+ });
26
+ }
27
+
28
+ await page.goto(domain, { waitUntil: "domcontentloaded" });
29
+
30
+ await page.addScriptTag({ url: "https://www.google.com/recaptcha/api.js?render=" + siteKey });
31
+
32
+ const token = await page.evaluate(async (siteKey, action) => {
33
+ return await grecaptcha.execute(siteKey, { action: action || "login" });
34
+ }, siteKey, action);
35
+
36
+ isResolved = true;
37
+ clearTimeout(cl);
38
+
39
+ if (!token || token.length < 10) {
40
+ reject(new Error("Failed to get token"));
41
+ } else {
42
+ resolve({ token: token, type: "recaptcha3" });
43
+ }
44
+ } catch (e) {
45
+ if (!isResolved) {
46
+ isResolved = true;
47
+ clearTimeout(cl);
48
+ reject(e);
49
+ }
50
+ }
51
+ });
52
+ }
53
+
54
+ module.exports = recaptchaV3;
Api/Api/turnstile.js ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * API_TURNSTILE
3
+ */
4
+ async function turnstile({ domain, siteKey, action, proxy }, page) {
5
+ if (!domain) throw new Error("Missing domain parameter");
6
+ if (!siteKey) throw new Error("Missing siteKey parameter");
7
+
8
+ const timeout = global.timeOut;
9
+
10
+ return new Promise(async (resolve, reject) => {
11
+ let isResolved = false;
12
+
13
+ const cl = setTimeout(() => {
14
+ if (!isResolved) {
15
+ isResolved = true;
16
+ reject(new Error("Timeout Error"));
17
+ }
18
+ }, timeout);
19
+
20
+ try {
21
+ if (proxy?.username && proxy?.password) {
22
+ await page.authenticate({
23
+ username: proxy.username,
24
+ password: proxy.password,
25
+ });
26
+ }
27
+
28
+ const htmlContent = `
29
+ <!DOCTYPE html>
30
+ <html lang="en">
31
+ <body>
32
+ <div class="turnstile"></div>
33
+ <script src="https://challenges.cloudflare.com/turnstile/v0/api.js?onload=onloadTurnstileCallback" defer></script>
34
+ <script>
35
+ window.onloadTurnstileCallback = function () {
36
+ turnstile.render('.turnstile', {
37
+ sitekey: '${siteKey}',
38
+ action: '${action || ""}',
39
+ callback: function (token) {
40
+ var c = document.createElement('input');
41
+ c.type = 'hidden';
42
+ c.name = 'cf-response';
43
+ c.value = token;
44
+ document.body.appendChild(c);
45
+ },
46
+ });
47
+ };
48
+ </script>
49
+ </body>
50
+ </html>
51
+ `;
52
+
53
+ await page.setRequestInterception(true);
54
+ page.removeAllListeners("request");
55
+ page.on("request", async (request) => {
56
+ if ([domain, domain + "/"].includes(request.url()) && request.resourceType() === "document") {
57
+ await request.respond({
58
+ status: 200,
59
+ contentType: "text/html",
60
+ body: htmlContent,
61
+ });
62
+ } else {
63
+ await request.continue();
64
+ }
65
+ });
66
+
67
+ await page.goto(domain, { waitUntil: "domcontentloaded" });
68
+ await page.waitForSelector('[name="cf-response"]', { timeout });
69
+
70
+ const token = await page.evaluate(() => {
71
+ return document.querySelector('[name="cf-response"]').value;
72
+ });
73
+
74
+ isResolved = true;
75
+ clearTimeout(cl);
76
+
77
+ if (!token || token.length < 10) {
78
+ reject(new Error("Failed to get token"));
79
+ } else {
80
+ resolve({ token: token, type: 'turnstile' });
81
+ }
82
+ } catch (e) {
83
+ if (!isResolved) {
84
+ isResolved = true;
85
+ clearTimeout(cl);
86
+ reject(e);
87
+ }
88
+ }
89
+ });
90
+ }
91
+
92
+ module.exports = turnstile;
Api/Dockerfile ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM node:20-slim
2
+
3
+ RUN apt update && apt install -y \
4
+ wget gnupg ca-certificates xvfb \
5
+ fonts-liberation libappindicator3-1 libasound2 libatk-bridge2.0-0 \
6
+ libatk1.0-0 libxss1 libnss3 libxcomposite1 libxdamage1 libxrandr2 libgbm1 \
7
+ && wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb \
8
+ && apt install -y ./google-chrome-stable_current_amd64.deb \
9
+ && rm google-chrome-stable_current_amd64.deb
10
+
11
+ WORKDIR /A
12
+
13
+ RUN mkdir -p /A/Api && mkdir -p /A/cache
14
+
15
+ COPY package.json ./
16
+ RUN npm install
17
+
18
+ COPY . .
19
+
20
+ EXPOSE 7860
21
+
22
+ CMD rm -f /tmp/.X99-lock && \
23
+ Xvfb :99 -screen 0 1024x768x24 & \
24
+ export DISPLAY=:99 && \
25
+ npm start
Api/README.md ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Api
3
+ emoji: πŸ¦€
4
+ colorFrom: blue
5
+ colorTo: yellow
6
+ sdk: docker
7
+ pinned: false
8
+ license: mit
9
+ short_description: Api
10
+ ---
11
+
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
Api/package.json ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "api",
3
+ "version": "7.3.0",
4
+ "description": "gmxch_Api",
5
+ "scripts": {
6
+ "start": "node Api.js",
7
+ "dev": "nodemon Api.js"
8
+ },
9
+ "dependencies": {
10
+ "express": "^5.1.0",
11
+ "puppeteer-real-browser": "^1.4.0"
12
+ },
13
+ "devDependencies": {
14
+ "nodemon": "^3.1.10"
15
+ },
16
+ "license": "MIT"
17
+ }
Api/supervisord.conf ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [supervisord]
2
+ nodaemon=true
3
+ logfile=/var/log/supervisor/supervisord.log
4
+ pidfile=/var/run/supervisord.pid
5
+ childlogdir=/var/log/supervisor
6
+
7
+ [program:xvfb]
8
+ command=Xvfb :99 -screen 0 1024x768x24 -ac +extension GLX +render -noreset
9
+ autostart=true
10
+ autorestart=true
11
+ priority=100
12
+ stdout_logfile=/var/log/supervisor/xvfb.log
13
+ stderr_logfile=/var/log/supervisor/xvfb.err
14
+
15
+ [program:api]
16
+ command=npm start
17
+ directory=/app/api
18
+ environment=DISPLAY=":99",NODE_ENV="production"
19
+ autostart=true
20
+ autorestart=true
21
+ priority=200
22
+ stdout_logfile=/var/log/supervisor/api.log
23
+ stderr_logfile=/var/log/supervisor/api.err
24
+ startretries=3
25
+
26
+ [program:bot]
27
+ command=gunicorn --bind 0.0.0.0:7861 --workers 1 --threads 2 --timeout 120 app:app
28
+ directory=/app/bot
29
+ environment=DISPLAY=":99",PYTHONUNBUFFERED="1"
30
+ autostart=true
31
+ autorestart=true
32
+ priority=300
33
+ stdout_logfile=/var/log/supervisor/bot.log
34
+ stderr_logfile=/var/log/supervisor/bot.err
35
+
36
+ [program:nginx]
37
+ command=nginx -g 'daemon off;'
38
+ autostart=true
39
+ autorestart=true
40
+ priority=400
41
+ stdout_logfile=/var/log/supervisor/nginx.log
42
+ stderr_logfile=/var/log/supervisor/nginx.err
Dockerfile ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ============================================
2
+ # DOCKERFILE FAUCET BOT - FULL FIX
3
+ # ============================================
4
+ FROM python:3.9-slim
5
+
6
+ # -------------------------------
7
+ # 1. Install system dependencies + Xvfb
8
+ # -------------------------------
9
+ RUN apt-get update && apt-get install -y \
10
+ curl \
11
+ wget \
12
+ gnupg \
13
+ ca-certificates \
14
+ xz-utils \
15
+ xvfb \
16
+ fonts-liberation \
17
+ libnss3 \
18
+ libatk-bridge2.0-0 \
19
+ libx11-xcb1 \
20
+ libxcb-dri3-0 \
21
+ libxcomposite1 \
22
+ libxdamage1 \
23
+ libxrandr2 \
24
+ libgbm1 \
25
+ libasound2 \
26
+ libatk1.0-0 \
27
+ libgtk-3-0 \
28
+ libxshmfence1 \
29
+ && rm -rf /var/lib/apt/lists/*
30
+
31
+ # -------------------------------
32
+ # 2. Install Google Chrome
33
+ # -------------------------------
34
+ RUN mkdir -p /etc/apt/keyrings \
35
+ && wget -q -O - https://dl.google.com/linux/linux_signing_key.pub > /tmp/google.pub \
36
+ && gpg --dearmor < /tmp/google.pub > /etc/apt/keyrings/google-chrome.gpg \
37
+ && echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/google-chrome.gpg] http://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google-chrome.list \
38
+ && apt-get update \
39
+ && apt-get install -y google-chrome-stable \
40
+ && rm -rf /var/lib/apt/lists/* \
41
+ && rm /tmp/google.pub
42
+
43
+ WORKDIR /app
44
+
45
+ # -------------------------------
46
+ # 3. Install Python dependencies
47
+ # -------------------------------
48
+ COPY requirements.txt .
49
+ RUN pip install --no-cache-dir --upgrade pip && \
50
+ pip install --no-cache-dir -r requirements.txt
51
+
52
+ # -------------------------------
53
+ # 4. Install Node.js 20
54
+ # -------------------------------
55
+ RUN mkdir -p /etc/apt/keyrings \
56
+ && curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \
57
+ && echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" > /etc/apt/sources.list.d/nodesource.list \
58
+ && apt-get update \
59
+ && apt-get install -y nodejs \
60
+ && rm -rf /var/lib/apt/lists/*
61
+
62
+ # -------------------------------
63
+ # 5. Copy API
64
+ # -------------------------------
65
+ COPY Api/ /app/Api/
66
+ WORKDIR /app/Api
67
+ RUN npm install --omit=dev
68
+
69
+ WORKDIR /app
70
+
71
+ # -------------------------------
72
+ # 6. Copy Python bot
73
+ # -------------------------------
74
+ COPY app.py .
75
+
76
+ # -------------------------------
77
+ # 7. Startup Script
78
+ # -------------------------------
79
+ RUN echo '#!/bin/bash\n\
80
+ echo "πŸ€– STARTING FAUCET BOT"\n\
81
+ echo "======================"\n\
82
+ \n\
83
+ echo "Starting Node.js API..."\n\
84
+ cd /app/Api\n\
85
+ node Api.js &\n\
86
+ \n\
87
+ echo "Waiting for API to start..."\n\
88
+ sleep 5\n\
89
+ \n\
90
+ echo "Starting Python Bot..."\n\
91
+ cd /app\n\
92
+ xvfb-run -a python3 app.py\n\
93
+ ' > /start.sh && chmod +x /start.sh
94
+
95
+ # -------------------------------
96
+ # 8. Healthcheck
97
+ # -------------------------------
98
+ HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
99
+ CMD curl -f http://localhost:3000/ || exit 1
100
+
101
+ # -------------------------------
102
+ # 9. Expose API port
103
+ # -------------------------------
104
+ EXPOSE 3000
105
+
106
+ # -------------------------------
107
+ # 10. Start container
108
+ # -------------------------------
109
+ CMD ["/start.sh"]
README.md CHANGED
@@ -1,8 +1,8 @@
1
  ---
2
- title: '270'
3
- emoji: πŸ“š
4
- colorFrom: indigo
5
- colorTo: yellow
6
  sdk: docker
7
  pinned: false
8
  ---
 
1
  ---
2
+ title: Auto
3
+ emoji: πŸ“‰
4
+ colorFrom: pink
5
+ colorTo: gray
6
  sdk: docker
7
  pinned: false
8
  ---
app.py ADDED
@@ -0,0 +1,934 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ MULTI-COIN FAUCET BOT - INDEPENDENT THREADS
4
+ Auto claim untuk 3 coin: LTC, DOGE, TRUMP
5
+ Setiap coin berjalan di thread terpisah dengan cooldown sendiri
6
+ """
7
+
8
+ import requests
9
+ import json
10
+ import time
11
+ import re
12
+ import sys
13
+ import os
14
+ import threading
15
+ from datetime import datetime, timedelta
16
+
17
+ class Color:
18
+ """Class untuk warna terminal"""
19
+ PURPLE = '\033[95m'
20
+ CYAN = '\033[96m'
21
+ BLUE = '\033[94m'
22
+ GREEN = '\033[92m'
23
+ YELLOW = '\033[93m'
24
+ RED = '\033[91m'
25
+ BOLD = '\033[1m'
26
+ END = '\033[0m'
27
+
28
+ class IndependentMultiCoinFaucetBot:
29
+ def __init__(self, email):
30
+ self.email = email
31
+ self.base_url = "https://litecoin.freepepecoin.com"
32
+ self.solver_url = "https://gi2h-xxx.hf.space"
33
+ self.solver_key = "00000000000000000000#0000000000000000000#000000000000000000#"
34
+
35
+ # File cookies per coin
36
+ self.cookies_files = {
37
+ 'LTC': "cookies_ltc.json",
38
+ 'DOGE': "cookies_doge.json",
39
+ 'TRUMP': "cookies_trump.json"
40
+ }
41
+
42
+ # Konfigurasi coin dengan thread control
43
+ self.coins = {
44
+ 'LTC': {
45
+ 'name': 'Litecoin',
46
+ 'url_path': '/faucet/LTC',
47
+ 'default_cooldown': 180, # 3 menit
48
+ 'cooldown': 180,
49
+ 'last_claim': 0,
50
+ 'success_count': 0,
51
+ 'total_claims': 0,
52
+ 'total_rewards': 0,
53
+ 'session': requests.Session(),
54
+ 'running': True,
55
+ 'last_login_time': 0,
56
+ 'thread': None
57
+ },
58
+ 'DOGE': {
59
+ 'name': 'Dogecoin',
60
+ 'url_path': '/faucet/DOGE',
61
+ 'default_cooldown': 30, # 30 detik
62
+ 'cooldown': 30,
63
+ 'last_claim': 0,
64
+ 'success_count': 0,
65
+ 'total_claims': 0,
66
+ 'total_rewards': 0,
67
+ 'session': requests.Session(),
68
+ 'running': True,
69
+ 'last_login_time': 0,
70
+ 'thread': None
71
+ },
72
+ 'TRUMP': {
73
+ 'name': 'Trumpcoin',
74
+ 'url_path': '/faucet/TRUMP',
75
+ 'default_cooldown': 10, # 10 detik
76
+ 'cooldown': 10,
77
+ 'last_claim': 0,
78
+ 'success_count': 0,
79
+ 'total_claims': 0,
80
+ 'total_rewards': 0,
81
+ 'session': requests.Session(),
82
+ 'running': True,
83
+ 'last_login_time': 0,
84
+ 'thread': None
85
+ }
86
+ }
87
+
88
+ # Setup headers untuk setiap session
89
+ for coin_symbol, coin_data in self.coins.items():
90
+ self.setup_headers(coin_data['session'])
91
+
92
+ # Statistik global
93
+ self.total_cycles = 0
94
+ self.stats_file = "stats_independent.json"
95
+ self.load_stats()
96
+
97
+ # Logging
98
+ self.log_dir = "logs"
99
+ os.makedirs(self.log_dir, exist_ok=True)
100
+
101
+ # Lock untuk print output agar tidak tercampur
102
+ self.print_lock = threading.Lock()
103
+
104
+ # Main running flag
105
+ self.running = True
106
+
107
+ def setup_headers(self, session):
108
+ """Set headers untuk meniru browser"""
109
+ session.headers.update({
110
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36',
111
+ 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
112
+ 'Accept-Language': 'id-ID,id;q=0.9,en-US;q=0.8,en;q=0.7',
113
+ 'Accept-Encoding': 'gzip, deflate, br',
114
+ 'Connection': 'keep-alive',
115
+ 'Upgrade-Insecure-Requests': '1',
116
+ 'sec-ch-ua': '"Not(A:Brand";v="8", "Chromium";v="144", "Google Chrome";v="144"',
117
+ 'sec-ch-ua-mobile': '?0',
118
+ 'sec-ch-ua-platform': '"Windows"',
119
+ })
120
+
121
+ def save_cookies(self, coin_symbol):
122
+ """Simpan cookies ke file untuk coin tertentu"""
123
+ try:
124
+ coin_data = self.coins[coin_symbol]
125
+ cookies_dict = requests.utils.dict_from_cookiejar(coin_data['session'].cookies)
126
+
127
+ with open(self.cookies_files[coin_symbol], 'w') as f:
128
+ json.dump({
129
+ 'cookies': cookies_dict,
130
+ 'saved_at': datetime.now().isoformat(),
131
+ 'email': self.email,
132
+ 'last_login_time': coin_data['last_login_time']
133
+ }, f, indent=2)
134
+
135
+ with self.print_lock:
136
+ print(f"{Color.GREEN}βœ“ [{coin_symbol}] Cookies disimpan{Color.END}")
137
+ return True
138
+ except Exception as e:
139
+ with self.print_lock:
140
+ print(f"{Color.YELLOW}⚠️ [{coin_symbol}] Gagal menyimpan cookies: {e}{Color.END}")
141
+ return False
142
+
143
+ def load_cookies(self, coin_symbol):
144
+ """Load cookies dari file untuk coin tertentu"""
145
+ try:
146
+ if os.path.exists(self.cookies_files[coin_symbol]):
147
+ with open(self.cookies_files[coin_symbol], 'r') as f:
148
+ data = json.load(f)
149
+ cookies_dict = data.get('cookies', {})
150
+
151
+ # Buat cookies jar dari dictionary
152
+ cookies_jar = requests.utils.cookiejar_from_dict(cookies_dict)
153
+ self.coins[coin_symbol]['session'].cookies = cookies_jar
154
+
155
+ # Update last login time
156
+ saved_time = data.get('saved_at')
157
+ if saved_time:
158
+ try:
159
+ saved_dt = datetime.fromisoformat(saved_time)
160
+ self.coins[coin_symbol]['last_login_time'] = saved_dt.timestamp()
161
+ except:
162
+ self.coins[coin_symbol]['last_login_time'] = time.time()
163
+ else:
164
+ self.coins[coin_symbol]['last_login_time'] = time.time()
165
+
166
+ with self.print_lock:
167
+ print(f"{Color.GREEN}βœ“ [{coin_symbol}] Cookies dimuat{Color.END}")
168
+ return True
169
+ else:
170
+ with self.print_lock:
171
+ print(f"{Color.YELLOW}⚠️ [{coin_symbol}] File cookies tidak ditemukan{Color.END}")
172
+ return False
173
+ except Exception as e:
174
+ with self.print_lock:
175
+ print(f"{Color.YELLOW}⚠️ [{coin_symbol}] Gagal memuat cookies: {e}{Color.END}")
176
+ return False
177
+
178
+ def check_login_status(self, coin_symbol):
179
+ """Cek status login dengan cookies untuk coin tertentu"""
180
+ coin_data = self.coins[coin_symbol]
181
+
182
+ try:
183
+ response = coin_data['session'].get(
184
+ f'{self.base_url}/dashboard',
185
+ timeout=30,
186
+ allow_redirects=True
187
+ )
188
+
189
+ if response.status_code == 200 and f"Welcome, <b>{self.email}</b>" in response.text:
190
+ with self.print_lock:
191
+ print(f"{Color.GREEN}βœ… [{coin_symbol}] Login status valid{Color.END}")
192
+ return True
193
+ else:
194
+ with self.print_lock:
195
+ print(f"{Color.YELLOW}⚠️ [{coin_symbol}] Cookies expired atau login diperlukan{Color.END}")
196
+ return False
197
+ except Exception as e:
198
+ with self.print_lock:
199
+ print(f"{Color.YELLOW}⚠️ [{coin_symbol}] Error cek login status: {e}{Color.END}")
200
+ return False
201
+
202
+ def need_relogin(self, coin_symbol):
203
+ """Cek apakah perlu relogin (setiap 6 jam) untuk coin tertentu"""
204
+ coin_data = self.coins[coin_symbol]
205
+ current_time = time.time()
206
+
207
+ if coin_data['last_login_time'] == 0:
208
+ return True
209
+
210
+ time_since_login = current_time - coin_data['last_login_time']
211
+ if time_since_login >= (6 * 3600): # 6 jam
212
+ hours = int(time_since_login // 3600)
213
+ minutes = int((time_since_login % 3600) // 60)
214
+ with self.print_lock:
215
+ print(f"{Color.YELLOW}⏰ [{coin_symbol}] Sudah {hours} jam {minutes} menit sejak login terakhir{Color.END}")
216
+ return True
217
+
218
+ return False
219
+
220
+ def print_banner(self):
221
+ """Menampilkan banner"""
222
+ banner = f"""
223
+ {Color.PURPLE}{Color.BOLD}
224
+ ╔══════════════════════════════════════════════════════════════╗
225
+ β•‘ INDEPENDENT MULTI-COIN FAUCET BOT V3.0 β•‘
226
+ β•‘ (Setiap coin berjalan di thread terpisah) β•‘
227
+ β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
228
+ {Color.END}
229
+ {Color.CYAN}Email: {self.email}{Color.END}
230
+ {Color.CYAN}Base URL: {self.base_url}{Color.END}
231
+ {Color.CYAN}Total Coins: {len(self.coins)} (LTC, DOGE, TRUMP){Color.END}
232
+ {Color.CYAN}Mode: INDEPENDENT THREADS - Tidak saling menunggu{Color.END}
233
+ """
234
+ print(banner)
235
+ self.print_coin_status_table()
236
+
237
+ def print_coin_status_table(self):
238
+ """Print tabel status semua coin"""
239
+ print(f"{Color.BLUE}╔══════════════════════════════════════════════════════════════╗{Color.END}")
240
+ print(f"{Color.BLUE}β•‘ COIN STATUS TABLE β•‘{Color.END}")
241
+ print(f"{Color.BLUE}╠═══════════╦══════════╦══════════���══════════════╦══════════════╣{Color.END}")
242
+ print(f"{Color.BLUE}β•‘ Coin β•‘ Cooldown β•‘ Status β•‘ Last Claim β•‘ Next Claim β•‘{Color.END}")
243
+ print(f"{Color.BLUE}╠═══════════╬══════════╬══════════╬══════════════╬══════════════╣{Color.END}")
244
+
245
+ for coin_symbol, coin_data in self.coins.items():
246
+ current_time = time.time()
247
+ time_since_last = current_time - coin_data['last_claim']
248
+ remaining = coin_data['cooldown'] - time_since_last
249
+
250
+ if coin_data['last_claim'] == 0:
251
+ status = f"{Color.GREEN}READY{Color.END}"
252
+ last_claim_str = "Never"
253
+ next_claim_str = "NOW"
254
+ elif remaining <= 0:
255
+ status = f"{Color.GREEN}READY{Color.END}"
256
+ last_claim_str = time.strftime("%H:%M:%S", time.localtime(coin_data['last_claim']))
257
+ next_claim_str = "NOW"
258
+ else:
259
+ mins = int(remaining // 60)
260
+ secs = int(remaining % 60)
261
+ status = f"{Color.YELLOW}WAIT {mins:02d}:{secs:02d}{Color.END}"
262
+ last_claim_str = time.strftime("%H:%M:%S", time.localtime(coin_data['last_claim']))
263
+ next_claim_time = coin_data['last_claim'] + coin_data['cooldown']
264
+ next_claim_str = time.strftime("%H:%M:%S", time.localtime(next_claim_time))
265
+
266
+ print(f"β•‘ {coin_symbol:^9} β•‘ {coin_data['cooldown']:^8} β•‘ {status:^8} β•‘ {last_claim_str:^12} β•‘ {next_claim_str:^12} β•‘")
267
+
268
+ print(f"{Color.BLUE}β•šβ•β•β•β•β•β•β•β•β•β•β•β•©β•β•β•β•β•β•β•β•β•β•β•©β•β•β•β•β•β•β•β•β•β•β•©β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•©β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•{Color.END}")
269
+ print()
270
+
271
+ def save_stats(self):
272
+ """Simpan statistik ke file"""
273
+ stats = {
274
+ 'last_update': datetime.now().isoformat(),
275
+ 'total_cycles': self.total_cycles,
276
+ 'coins': {},
277
+ 'email': self.email
278
+ }
279
+
280
+ for coin_symbol, coin_data in self.coins.items():
281
+ stats['coins'][coin_symbol] = {
282
+ 'last_claim': coin_data['last_claim'],
283
+ 'success_count': coin_data['success_count'],
284
+ 'total_claims': coin_data['total_claims'],
285
+ 'total_rewards': coin_data['total_rewards'],
286
+ 'cooldown': coin_data['cooldown'],
287
+ 'last_login_time': coin_data['last_login_time']
288
+ }
289
+
290
+ try:
291
+ with open(self.stats_file, 'w') as f:
292
+ json.dump(stats, f, indent=2)
293
+ except Exception as e:
294
+ with self.print_lock:
295
+ print(f"{Color.YELLOW}⚠️ Gagal menyimpan statistik: {e}{Color.END}")
296
+
297
+ def load_stats(self):
298
+ """Load statistik dari file"""
299
+ try:
300
+ if os.path.exists(self.stats_file):
301
+ with open(self.stats_file, 'r') as f:
302
+ stats = json.load(f)
303
+ self.total_cycles = stats.get('total_cycles', 0)
304
+
305
+ saved_coins = stats.get('coins', {})
306
+ for coin_symbol in self.coins.keys():
307
+ if coin_symbol in saved_coins:
308
+ for key in ['last_claim', 'success_count', 'total_claims',
309
+ 'total_rewards', 'cooldown', 'last_login_time']:
310
+ if key in saved_coins[coin_symbol]:
311
+ self.coins[coin_symbol][key] = saved_coins[coin_symbol][key]
312
+
313
+ except Exception as e:
314
+ with self.print_lock:
315
+ print(f"{Color.YELLOW}⚠️ Gagal load statistik: {e}{Color.END}")
316
+
317
+ def get_recaptcha_token(self, coin_symbol):
318
+ """Mendapatkan token reCAPTCHA dari solver"""
319
+ with self.print_lock:
320
+ print(f"{Color.CYAN}[{coin_symbol}] [1/3] Meminta token reCAPTCHA...{Color.END}")
321
+
322
+ headers = {
323
+ "Content-Type": "application/json",
324
+ "key": self.solver_key
325
+ }
326
+
327
+ data = {
328
+ "type": "recaptcha3",
329
+ "domain": self.base_url,
330
+ "siteKey": "6LcbMB0sAAAAAAxsy76NqLNBhHfzZO8E4jLJ8XNl"
331
+ }
332
+
333
+ try:
334
+ # Submit task
335
+ response = requests.post(
336
+ f"{self.solver_url}/solve",
337
+ headers=headers,
338
+ json=data,
339
+ timeout=30
340
+ )
341
+
342
+ result = response.json()
343
+ if "taskId" not in result:
344
+ with self.print_lock:
345
+ print(f"{Color.RED}❌ [{coin_symbol}] Gagal mendapatkan Task ID{Color.END}")
346
+ return None
347
+
348
+ task_id = result["taskId"]
349
+ with self.print_lock:
350
+ print(f"{Color.GREEN}βœ“ [{coin_symbol}] Task ID: {task_id}{Color.END}")
351
+
352
+ # Polling untuk hasil
353
+ for i in range(30):
354
+ time.sleep(2)
355
+
356
+ poll_data = {"taskId": task_id}
357
+ poll_response = requests.post(
358
+ f"{self.solver_url}/solve",
359
+ headers=headers,
360
+ json=poll_data,
361
+ timeout=30
362
+ )
363
+
364
+ poll_result = poll_response.json()
365
+
366
+ if poll_result.get("status") == "done":
367
+ with self.print_lock:
368
+ print(f"{Color.GREEN}βœ“ [{coin_symbol}] reCAPTCHA solved!{Color.END}")
369
+ token = poll_result.get("token") or poll_result.get("solution", {}).get("token")
370
+ if token:
371
+ return token
372
+
373
+ elif poll_result.get("status") == "error":
374
+ with self.print_lock:
375
+ print(f"{Color.RED}❌ [{coin_symbol}] Error dari solver{Color.END}")
376
+ return None
377
+
378
+ with self.print_lock:
379
+ print(f"{Color.RED}❌ [{coin_symbol}] Timeout{Color.END}")
380
+ return None
381
+
382
+ except Exception as e:
383
+ with self.print_lock:
384
+ print(f"{Color.RED}❌ [{coin_symbol}] Error: {e}{Color.END}")
385
+ return None
386
+
387
+ def login(self, coin_symbol):
388
+ """Login ke website untuk mendapatkan cookies untuk coin tertentu"""
389
+ coin_data = self.coins[coin_symbol]
390
+
391
+ with self.print_lock:
392
+ print(f"{Color.CYAN}[{coin_symbol}] [1/4] Login process...{Color.END}")
393
+
394
+ # Dapatkan token reCAPTCHA untuk login
395
+ recaptcha_token = self.get_recaptcha_token(coin_symbol)
396
+ if not recaptcha_token:
397
+ with self.print_lock:
398
+ print(f"{Color.RED}❌ [{coin_symbol}] Gagal mendapatkan token reCAPTCHA untuk login{Color.END}")
399
+ return False
400
+
401
+ # Persiapkan data login
402
+ headers = {
403
+ 'Content-Type': 'application/x-www-form-urlencoded',
404
+ 'Origin': self.base_url,
405
+ 'Referer': f'{self.base_url}/',
406
+ 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8',
407
+ }
408
+
409
+ data = {
410
+ 'address': self.email,
411
+ 'g-recaptcha-response': recaptcha_token
412
+ }
413
+
414
+ try:
415
+ with self.print_lock:
416
+ print(f"{Color.CYAN}[{coin_symbol}] [2/4] Mengirim data login...{Color.END}")
417
+
418
+ response = coin_data['session'].post(
419
+ f'{self.base_url}/',
420
+ headers=headers,
421
+ data=data,
422
+ timeout=30,
423
+ allow_redirects=True
424
+ )
425
+
426
+ with self.print_lock:
427
+ print(f"{Color.CYAN}βœ“ [{coin_symbol}] Response status: {response.status_code}{Color.END}")
428
+
429
+ # Cek apakah login berhasil
430
+ if f"Welcome, <b>{self.email}</b>" in response.text:
431
+ with self.print_lock:
432
+ print(f"{Color.GREEN}βœ… [{coin_symbol}] Login berhasil!{Color.END}")
433
+
434
+ # Update waktu login terakhir
435
+ coin_data['last_login_time'] = time.time()
436
+
437
+ # Simpan cookies
438
+ self.save_cookies(coin_symbol)
439
+
440
+ return True
441
+ else:
442
+ with self.print_lock:
443
+ print(f"{Color.RED}❌ [{coin_symbol}] Login gagal!{Color.END}")
444
+ return False
445
+
446
+ except Exception as e:
447
+ with self.print_lock:
448
+ print(f"{Color.RED}❌ [{coin_symbol}] Error login: {e}{Color.END}")
449
+ return False
450
+
451
+ def relogin_if_needed(self, coin_symbol):
452
+ """Relogin jika diperlukan (setiap 6 jam) untuk coin tertentu"""
453
+ if self.need_relogin(coin_symbol):
454
+ with self.print_lock:
455
+ print(f"{Color.YELLOW}πŸ”„ [{coin_symbol}] Waktunya relogin (setiap 6 jam)...{Color.END}")
456
+ return self.login(coin_symbol)
457
+ return True
458
+
459
+ def get_csrf_token(self, coin_symbol):
460
+ """Ambil CSRF token dari halaman faucet untuk coin tertentu"""
461
+ coin_data = self.coins[coin_symbol]
462
+
463
+ with self.print_lock:
464
+ print(f"{Color.CYAN}[{coin_symbol}] [2/4] Mengambil CSRF token...{Color.END}")
465
+
466
+ headers = {
467
+ 'Referer': f'{self.base_url}/dashboard',
468
+ 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8',
469
+ }
470
+
471
+ try:
472
+ # Batasi redirects
473
+ coin_data['session'].max_redirects = 5
474
+
475
+ response = coin_data['session'].get(
476
+ f'{self.base_url}{coin_data["url_path"]}',
477
+ headers=headers,
478
+ timeout=30,
479
+ allow_redirects=True
480
+ )
481
+
482
+ with self.print_lock:
483
+ print(f"{Color.CYAN}βœ“ [{coin_symbol}] Response status: {response.status_code}{Color.END}")
484
+
485
+ if response.status_code != 200:
486
+ with self.print_lock:
487
+ print(f"{Color.RED}❌ [{coin_symbol}] Gagal akses faucet: {response.status_code}{Color.END}")
488
+ return None
489
+
490
+ # Cari CSRF token dengan regex
491
+ csrf_pattern = r'name="csrf_token" value="([a-f0-9]{64})"'
492
+ match = re.search(csrf_pattern, response.text)
493
+
494
+ if match:
495
+ csrf_token = match.group(1)
496
+ with self.print_lock:
497
+ print(f"{Color.GREEN}βœ“ [{coin_symbol}] CSRF token ditemukan: {csrf_token[:16]}...{Color.END}")
498
+ return csrf_token
499
+ else:
500
+ with self.print_lock:
501
+ print(f"{Color.RED}❌ [{coin_symbol}] CSRF token tidak ditemukan{Color.END}")
502
+ return None
503
+
504
+ except requests.exceptions.TooManyRedirects:
505
+ with self.print_lock:
506
+ print(f"{Color.RED}❌ [{coin_symbol}] Redirect loop terdeteksi!{Color.END}")
507
+ return None
508
+ except Exception as e:
509
+ with self.print_lock:
510
+ print(f"{Color.RED}❌ [{coin_symbol}] Error: {e}{Color.END}")
511
+ return None
512
+
513
+ def extract_cooldown_from_response(self, coin_symbol, response_text):
514
+ """Ekstrak waktu cooldown dari HTML response"""
515
+ try:
516
+ # Pola pencarian cooldown
517
+ patterns = [
518
+ r'cooldown.*?(\d+)\s*(minute|min|second|sec|hour|hr)',
519
+ r'wait.*?(\d+)\s*(minute|min|second|sec|hour|hr)',
520
+ r'timer.*?(\d+)\s*(minute|min|second|sec|hour|hr)',
521
+ r'(\d+)\s*(minute|min|second|sec|hour|hr).*?cooldown',
522
+ r'(\d+)\s*(minute|min|second|sec|hour|hr).*?wait'
523
+ ]
524
+
525
+ for pattern in patterns:
526
+ matches = re.findall(pattern, response_text, re.IGNORECASE)
527
+ if matches:
528
+ for match in matches:
529
+ value = int(match[0])
530
+ unit = match[1].lower()
531
+
532
+ # Konversi ke detik
533
+ if 'hour' in unit or 'hr' in unit:
534
+ return value * 3600
535
+ elif 'minute' in unit or 'min' in unit:
536
+ return value * 60
537
+ elif 'second' in unit or 'sec' in unit:
538
+ return value
539
+
540
+ return None # Tidak ditemukan cooldown
541
+
542
+ except Exception as e:
543
+ with self.print_lock:
544
+ print(f"{Color.YELLOW}⚠️ [{coin_symbol}] Error extracting cooldown: {e}{Color.END}")
545
+ return None
546
+
547
+ def claim_coin(self, coin_symbol):
548
+ """Klaim faucet untuk coin tertentu"""
549
+ coin_data = self.coins[coin_symbol]
550
+
551
+ with self.print_lock:
552
+ print(f"\n{Color.BLUE}{'═' * 50}{Color.END}")
553
+ print(f"{Color.BOLD}πŸͺ™ [{coin_symbol}] CLAIMING {coin_data['name']}{Color.END}")
554
+ print(f"{Color.BLUE}{'═' * 50}{Color.END}")
555
+
556
+ # 1. Ambil CSRF token
557
+ csrf_token = self.get_csrf_token(coin_symbol)
558
+ if not csrf_token:
559
+ with self.print_lock:
560
+ print(f"{Color.RED}❌ [{coin_symbol}] Gagal mendapatkan CSRF token{Color.END}")
561
+ return False, None
562
+
563
+ # 2. Dapatkan token reCAPTCHA untuk claim
564
+ with self.print_lock:
565
+ print(f"{Color.CYAN}[{coin_symbol}] [3/4] Meminta token reCAPTCHA untuk claim...{Color.END}")
566
+
567
+ recaptcha_token = self.get_recaptcha_token(coin_symbol)
568
+ if not recaptcha_token:
569
+ with self.print_lock:
570
+ print(f"{Color.RED}❌ [{coin_symbol}] Gagal mendapatkan token reCAPTCHA untuk claim{Color.END}")
571
+ return False, None
572
+
573
+ # 3. Persiapkan data claim
574
+ headers = {
575
+ 'Content-Type': 'application/x-www-form-urlencoded',
576
+ 'Origin': self.base_url,
577
+ 'Referer': f'{self.base_url}{coin_data["url_path"]}',
578
+ 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8',
579
+ }
580
+
581
+ data = {
582
+ 'currency': coin_symbol,
583
+ 'csrf_token': csrf_token,
584
+ 'g-recaptcha-response': recaptcha_token,
585
+ 'claim': ''
586
+ }
587
+
588
+ try:
589
+ with self.print_lock:
590
+ print(f"{Color.CYAN}[{coin_symbol}] [4/4] Mengirim claim...{Color.END}")
591
+
592
+ response = coin_data['session'].post(
593
+ f'{self.base_url}{coin_data["url_path"]}',
594
+ headers=headers,
595
+ data=data,
596
+ timeout=30,
597
+ allow_redirects=True
598
+ )
599
+
600
+ with self.print_lock:
601
+ print(f"{Color.CYAN}βœ“ [{coin_symbol}] Response status: {response.status_code}{Color.END}")
602
+
603
+ # Cek apakah claim berhasil
604
+ success_keywords = [
605
+ 'successfully', 'claimed', 'reward',
606
+ 'thank you', 'you received', 'has been added',
607
+ 'congratulation'
608
+ ]
609
+
610
+ response_lower = response.text.lower()
611
+ success = any(keyword in response_lower for keyword in success_keywords)
612
+
613
+ if success:
614
+ with self.print_lock:
615
+ print(f"{Color.GREEN}βœ… [{coin_symbol}] Claim berhasil!{Color.END}")
616
+
617
+ # Cari jumlah reward
618
+ reward_patterns = [
619
+ r'(\d+\.?\d*)\s*' + re.escape(coin_symbol),
620
+ r'claimed\s*:\s*(\d+\.?\d*)',
621
+ r'reward\s*:\s*(\d+\.?\d*)',
622
+ r'you received.*?(\d+\.?\d*)',
623
+ r'(\d+\.?\d*)\s*coins?'
624
+ ]
625
+
626
+ reward_found = 0
627
+ for pattern in reward_patterns:
628
+ match = re.search(pattern, response.text, re.IGNORECASE)
629
+ if match:
630
+ reward_found = float(match.group(1))
631
+ coin_data['total_rewards'] += reward_found
632
+ with self.print_lock:
633
+ print(f"{Color.GREEN}πŸ’° [{coin_symbol}] Reward: {reward_found} {coin_symbol}{Color.END}")
634
+ break
635
+
636
+ # Update statistik
637
+ coin_data['success_count'] += 1
638
+
639
+ # Update cookies setelah claim sukses
640
+ self.save_cookies(coin_symbol)
641
+
642
+ else:
643
+ with self.print_lock:
644
+ print(f"{Color.RED}❌ [{coin_symbol}] Claim gagal!{Color.END}")
645
+
646
+ # Cek error message
647
+ error_keywords = ['error', 'failed', 'try again', 'cooldown', 'already claimed', 'wait']
648
+ for keyword in error_keywords:
649
+ if keyword in response_lower:
650
+ with self.print_lock:
651
+ print(f"{Color.YELLOW}⚠️ [{coin_symbol}] Pesan: {keyword}{Color.END}")
652
+ break
653
+
654
+ # Update total claims
655
+ coin_data['total_claims'] += 1
656
+ coin_data['last_claim'] = time.time()
657
+
658
+ # Ekstrak cooldown dari response
659
+ extracted_cooldown = self.extract_cooldown_from_response(coin_symbol, response.text)
660
+ if extracted_cooldown:
661
+ coin_data['cooldown'] = extracted_cooldown
662
+ with self.print_lock:
663
+ print(f"{Color.CYAN}⏰ [{coin_symbol}] Cooldown terdeteksi: {extracted_cooldown} detik{Color.END}")
664
+ else:
665
+ # Gunakan default cooldown
666
+ coin_data['cooldown'] = coin_data['default_cooldown']
667
+ with self.print_lock:
668
+ print(f"{Color.CYAN}⏰ [{coin_symbol}] Menggunakan default cooldown: {coin_data['default_cooldown']} detik{Color.END}")
669
+
670
+ # Simpan statistik
671
+ self.save_stats()
672
+
673
+ return success, response.text
674
+
675
+ except Exception as e:
676
+ with self.print_lock:
677
+ print(f"{Color.RED}❌ [{coin_symbol}] Error saat claim: {e}{Color.END}")
678
+ return False, str(e)
679
+
680
+ def coin_worker(self, coin_symbol):
681
+ """Worker thread untuk setiap coin - BERJALAN INDEPENDEN"""
682
+ coin_data = self.coins[coin_symbol]
683
+
684
+ with self.print_lock:
685
+ print(f"{Color.CYAN}πŸš€ [{coin_symbol}] Thread dimulai!{Color.END}")
686
+
687
+ # Cek cookies terlebih dahulu
688
+ if self.load_cookies(coin_symbol):
689
+ if not self.check_login_status(coin_symbol):
690
+ with self.print_lock:
691
+ print(f"{Color.YELLOW}⚠️ [{coin_symbol}] Cookies tidak valid, melakukan login...{Color.END}")
692
+ if not self.login(coin_symbol):
693
+ with self.print_lock:
694
+ print(f"{Color.RED}❌ [{coin_symbol}] Gagal login!{Color.END}")
695
+ return
696
+ else:
697
+ # Jika tidak ada cookies, lakukan login
698
+ with self.print_lock:
699
+ print(f"{Color.YELLOW}⚠️ [{coin_symbol}] Melakukan login...{Color.END}")
700
+ if not self.login(coin_symbol):
701
+ with self.print_lock:
702
+ print(f"{Color.RED}❌ [{coin_symbol}] Gagal login!{Color.END}")
703
+ return
704
+
705
+ # Main loop untuk coin ini
706
+ while self.running and coin_data['running']:
707
+ try:
708
+ current_time = time.time()
709
+ time_since_last = current_time - coin_data['last_claim']
710
+
711
+ # Jika belum pernah di-claim atau cooldown sudah selesai
712
+ if coin_data['last_claim'] == 0 or time_since_last >= coin_data['cooldown']:
713
+ # Cek apakah perlu relogin
714
+ if not self.relogin_if_needed(coin_symbol):
715
+ with self.print_lock:
716
+ print(f"{Color.YELLOW}⚠️ [{coin_symbol}] Relogin gagal, tunggu 60 detik...{Color.END}")
717
+ time.sleep(60)
718
+ continue
719
+
720
+ # Lakukan claim
721
+ success, _ = self.claim_coin(coin_symbol)
722
+
723
+ if not success:
724
+ with self.print_lock:
725
+ print(f"{Color.YELLOW}⚠️ [{coin_symbol}] Claim gagal, coba login ulang dalam 10 detik...{Color.END}")
726
+ time.sleep(10)
727
+
728
+ # Coba login ulang
729
+ if not self.login(coin_symbol):
730
+ with self.print_lock:
731
+ print(f"{Color.RED}❌ [{coin_symbol}] Gagal login ulang, tunggu 60 detik...{Color.END}")
732
+ time.sleep(60)
733
+ else:
734
+ # Hitung waktu tunggu
735
+ wait_time = coin_data['cooldown'] - time_since_last
736
+
737
+ # Update status setiap 10 detik jika dalam cooldown
738
+ if wait_time > 10:
739
+ remaining = int(wait_time)
740
+ with self.print_lock:
741
+ print(f"{Color.YELLOW}⏳ [{coin_symbol}] Cooldown: {remaining} detik tersisa...{Color.END}")
742
+
743
+ # Sleep dengan interval 1 detik untuk bisa di-interrupt
744
+ for _ in range(min(remaining, 10)):
745
+ if not self.running or not coin_data['running']:
746
+ break
747
+ time.sleep(1)
748
+ else:
749
+ time.sleep(1)
750
+
751
+ except Exception as e:
752
+ with self.print_lock:
753
+ print(f"{Color.RED}❌ [{coin_symbol}] Error dalam worker thread: {e}{Color.END}")
754
+ time.sleep(10)
755
+
756
+ with self.print_lock:
757
+ print(f"{Color.YELLOW}πŸ›‘ [{coin_symbol}] Thread dihentikan{Color.END}")
758
+
759
+ def print_statistics(self):
760
+ """Tampilkan statistik semua coin"""
761
+ with self.print_lock:
762
+ print(f"\n{Color.PURPLE}{Color.BOLD}πŸ“Š STATISTIK SEMUA COIN{Color.END}")
763
+ print(f"{Color.BLUE}╔═══════════╦══════════╦══════════╦══════════╦══════════════╗{Color.END}")
764
+ print(f"{Color.BLUE}β•‘ Coin β•‘ Claims β•‘ Success β•‘ Reward β•‘ Success Rateβ•‘{Color.END}")
765
+ print(f"{Color.BLUE}╠═══════════╬══════════╬══════════╬══════════╬══════════════╣{Color.END}")
766
+
767
+ total_claims = 0
768
+ total_success = 0
769
+ total_rewards = 0
770
+
771
+ for coin_symbol, coin_data in self.coins.items():
772
+ claims = coin_data['total_claims']
773
+ success = coin_data['success_count']
774
+ rewards = coin_data['total_rewards']
775
+
776
+ if claims > 0:
777
+ success_rate = (success / claims) * 100
778
+ rate_str = f"{success_rate:.1f}%"
779
+ else:
780
+ rate_str = "0%"
781
+
782
+ with self.print_lock:
783
+ print(f"β•‘ {coin_symbol:^9} β•‘ {claims:^8} β•‘ {success:^8} β•‘ {rewards:^8.4f} β•‘ {rate_str:^12} β•‘")
784
+
785
+ total_claims += claims
786
+ total_success += success
787
+ total_rewards += rewards
788
+
789
+ with self.print_lock:
790
+ print(f"{Color.BLUE}╠═══════════╬══════════╬══════════╬══════════╬══════════════╣{Color.END}")
791
+
792
+ if total_claims > 0:
793
+ overall_rate = (total_success / total_claims) * 100
794
+ rate_str = f"{overall_rate:.1f}%"
795
+ else:
796
+ rate_str = "0%"
797
+
798
+ print(f"β•‘ {Color.BOLD}TOTAL{Color.END:^9} β•‘ {total_claims:^8} β•‘ {total_success:^8} β•‘ {total_rewards:^8.4f} β•‘ {rate_str:^12} β•‘")
799
+ print(f"{Color.BLUE}β•šβ•β•β•β•β•β•β•β•β•β•β•β•©β•β•β•β•β•β•β•β•β•β•β•©β•β•β•β•β•β•β•β•β•β•β•©β•β•β•β•β•β•β•β•β•β•β•©β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•{Color.END}")
800
+
801
+ # Tampilkan waktu sekarang
802
+ print(f"{Color.CYAN}πŸ• Waktu Sekarang: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}{Color.END}")
803
+ print()
804
+
805
+ def run(self, max_cycles=100000):
806
+ """Jalankan semua coin di thread terpisah"""
807
+ self.print_banner()
808
+
809
+ with self.print_lock:
810
+ print(f"{Color.GREEN}{'='*60}{Color.END}")
811
+ print(f"{Color.BOLD}πŸš€ MEMULAI INDEPENDENT THREADS UNTUK 3 COIN{Color.END}")
812
+ print(f"{Color.GREEN}{'='*60}{Color.END}")
813
+
814
+ # Buat thread untuk setiap coin
815
+ threads = []
816
+ for coin_symbol in self.coins.keys():
817
+ thread = threading.Thread(
818
+ target=self.coin_worker,
819
+ args=(coin_symbol,),
820
+ name=f"Thread-{coin_symbol}",
821
+ daemon=True
822
+ )
823
+ thread.start()
824
+ self.coins[coin_symbol]['thread'] = thread
825
+ threads.append(thread)
826
+
827
+ # Thread untuk update statistik periodik
828
+ def stats_updater():
829
+ while self.running:
830
+ time.sleep(60) # Update setiap 1 menit
831
+ if self.running:
832
+ self.print_statistics()
833
+ self.print_coin_status_table()
834
+ self.save_stats()
835
+
836
+ stats_thread = threading.Thread(target=stats_updater, daemon=True)
837
+ stats_thread.start()
838
+
839
+ try:
840
+ # Keep main thread alive
841
+ while self.running:
842
+ # Tampilkan status setiap 5 menit
843
+ time.sleep(300)
844
+
845
+ except KeyboardInterrupt:
846
+ with self.print_lock:
847
+ print(f"\n\n{Color.YELLOW}⚠️ Keyboard interrupt diterima, menghentikan semua thread...{Color.END}")
848
+
849
+ self.running = False
850
+
851
+ # Beri waktu untuk thread berhenti
852
+ with self.print_lock:
853
+ print(f"{Color.YELLOW}⏳ Menunggu thread berhenti...{Color.END}")
854
+
855
+ time.sleep(5)
856
+
857
+ finally:
858
+ # Hentikan semua thread
859
+ self.running = False
860
+ for coin_symbol, coin_data in self.coins.items():
861
+ coin_data['running'] = False
862
+
863
+ # Tunggu semua thread selesai
864
+ for thread in threads:
865
+ if thread.is_alive():
866
+ thread.join(timeout=10)
867
+
868
+ # Tampilkan statistik akhir
869
+ with self.print_lock:
870
+ print(f"\n{Color.PURPLE}{'='*60}{Color.END}")
871
+ print(f"{Color.BOLD}🏁 STATISTIK AKHIR{Color.END}")
872
+ print(f"{Color.PURPLE}{'='*60}{Color.END}")
873
+
874
+ self.print_statistics()
875
+ self.save_stats()
876
+
877
+ with self.print_lock:
878
+ print(f"{Color.GREEN}βœ… Semua thread dihentikan{Color.END}")
879
+
880
+ def main():
881
+ """Fungsi utama dengan infinite loop untuk Docker"""
882
+ print(f"{Color.CYAN}πŸš€ Starting Independent Multi-Coin Faucet Bot{Color.END}")
883
+
884
+ # Konfigurasi dari environment variables atau default
885
+ EMAIL = os.getenv('EMAIL', 'gi2h2705@gmail.com')
886
+
887
+ print(f"{Color.CYAN}πŸ“§ Email: {EMAIL}{Color.END}")
888
+ print(f"{Color.CYAN}⚑ Mode: INDEPENDENT THREADS (3 coin terpisah){Color.END}")
889
+ print(f"{Color.CYAN}⏰ Timezone: {os.getenv('TZ', 'Asia/Jakarta')}{Color.END}")
890
+ print(f"{Color.CYAN}πŸͺ Cookies Management: Per-coin cookies{Color.END}")
891
+ print(f"{Color.CYAN}πŸ”„ Relogin Interval: Every 6 hours per coin{Color.END}\n")
892
+
893
+ consecutive_failures = 0
894
+ max_consecutive_failures = 3
895
+
896
+ while True:
897
+ try:
898
+ bot = IndependentMultiCoinFaucetBot(EMAIL)
899
+ bot.run()
900
+
901
+ consecutive_failures = 0
902
+ print(f"{Color.GREEN}βœ… All threads completed{Color.END}")
903
+
904
+ except KeyboardInterrupt:
905
+ print(f"\n{Color.YELLOW}πŸ‘‹ Bot stopped by user{Color.END}")
906
+ break
907
+ except Exception as e:
908
+ consecutive_failures += 1
909
+ print(f"{Color.RED}⚠️ Unexpected error: {e}{Color.END}")
910
+
911
+ if consecutive_failures >= max_consecutive_failures:
912
+ print(f"{Color.RED}⚠️ Too many consecutive failures, waiting 5 minutes...{Color.END}")
913
+ time.sleep(300) # Tunggu 5 menit
914
+ consecutive_failures = 0
915
+ else:
916
+ print(f"{Color.CYAN}⏳ Restarting in 30 seconds...{Color.END}")
917
+ time.sleep(30)
918
+
919
+ if __name__ == "__main__":
920
+ # Install dependencies jika diperlukan
921
+ try:
922
+ import requests
923
+ except ImportError:
924
+ print("Menginstall requests...")
925
+ import subprocess
926
+ subprocess.check_call(["pip", "install", "requests"])
927
+
928
+ # Jalankan
929
+ try:
930
+ main()
931
+ except KeyboardInterrupt:
932
+ print(f"\n\n{Color.YELLOW}πŸ‘‹ Sampai jumpa!{Color.END}")
933
+ except Exception as e:
934
+ print(f"\n{Color.RED}❌ Error: {e}{Color.END}")
docker-compose.yml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ multi-coin-bot:
5
+ build: .
6
+ container_name: multi-coin-faucet-bot
7
+ restart: unless-stopped
8
+ volumes:
9
+ - ./logs:/app/logs
10
+ - ./data:/app/data
11
+ environment:
12
+ - TZ=Asia/Jakarta
13
+ - EMAIL=gi2h27@gmail.com
14
+ - MAX_CYCLES=100000
15
+ - SOLVER_URL=https://gi2h-xxx.hf.space
16
+ - SOLVER_KEY=00000000000000000000#0000000000000000000#000000000000000000#
17
+ - PYTHONUNBUFFERED=1
18
+ logging:
19
+ driver: "json-file"
20
+ options:
21
+ max-size: "10m"
22
+ max-file: "3"
requirements.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ requests==2.31.0
2
+ colorama==0.4.6
3
+ python-dotenv==1.0.0