fourmovie commited on
Commit
10bc490
·
1 Parent(s): 83eeab2
Files changed (2) hide show
  1. endpoints/recaptchav2.js +277 -0
  2. index.js +20 -7
endpoints/recaptchav2.js ADDED
@@ -0,0 +1,277 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ async function recaptchaV2({ domain, proxy, siteKey, action = "submit", isInvisible = false }, page) {
2
+ if (!domain) throw new Error("Missing domain parameter");
3
+ if (!siteKey) throw new Error("Missing siteKey parameter");
4
+
5
+ const timeout = global.timeOut || 60000;
6
+ let isResolved = false;
7
+
8
+ const cl = setTimeout(async () => {
9
+ if (!isResolved) {
10
+ throw new Error("Timeout Error");
11
+ }
12
+ }, timeout);
13
+
14
+ try {
15
+ if (proxy?.username && proxy?.password) {
16
+ await page.authenticate({
17
+ username: proxy.username,
18
+ password: proxy.password,
19
+ });
20
+ }
21
+
22
+ const htmlContent = `
23
+ <!DOCTYPE html>
24
+ <html lang="en">
25
+ <head>
26
+ <meta charset="UTF-8">
27
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
28
+ <title>reCAPTCHA v2 Solver</title>
29
+ <style>
30
+ body {
31
+ font-family: Arial, sans-serif;
32
+ margin: 0;
33
+ padding: 20px;
34
+ background: #f5f5f5;
35
+ display: flex;
36
+ justify-content: center;
37
+ align-items: center;
38
+ min-height: 100vh;
39
+ }
40
+ .container {
41
+ background: white;
42
+ padding: 30px;
43
+ border-radius: 10px;
44
+ box-shadow: 0 2px 10px rgba(0,0,0,0.1);
45
+ text-align: center;
46
+ }
47
+ .status {
48
+ margin-top: 20px;
49
+ padding: 10px;
50
+ border-radius: 5px;
51
+ background: #f8f9fa;
52
+ }
53
+ button {
54
+ background: #007bff;
55
+ color: white;
56
+ border: none;
57
+ padding: 10px 20px;
58
+ border-radius: 5px;
59
+ cursor: pointer;
60
+ margin: 10px;
61
+ }
62
+ button:hover {
63
+ background: #0056b3;
64
+ }
65
+ </style>
66
+ </head>
67
+ <body>
68
+ <div class="container">
69
+ <h2>reCAPTCHA v2 Solver</h2>
70
+ <p>SiteKey: ${siteKey}</p>
71
+ <div id="recaptcha-container">
72
+ <div class="g-recaptcha"
73
+ data-sitekey="${siteKey}"
74
+ data-callback="recaptchaCallback"
75
+ data-expired-callback="recaptchaExpired"
76
+ data-error-callback="recaptchaError"
77
+ data-size="${isInvisible ? 'invisible' : 'normal'}"
78
+ data-theme="light">
79
+ </div>
80
+ </div>
81
+ ${isInvisible ? '<button onclick="executeInvisible()">Execute reCAPTCHA</button>' : ''}
82
+ <button onclick="checkToken()">Check Token</button>
83
+ <div class="status" id="status">Waiting for reCAPTCHA...</div>
84
+ </div>
85
+
86
+ <script>
87
+ // Global variables
88
+ window.recaptchaToken = null;
89
+ window.recaptchaSolved = false;
90
+
91
+ // Callback functions
92
+ window.recaptchaCallback = function(token) {
93
+ console.log('reCAPTCHA token received:', token);
94
+ window.recaptchaToken = token;
95
+ window.recaptchaSolved = true;
96
+
97
+ document.getElementById('status').innerHTML = '✅ reCAPTCHA Solved! Token: ' + token.substring(0, 20) + '...';
98
+ document.getElementById('status').style.background = '#d4edda';
99
+ document.getElementById('status').style.color = '#155724';
100
+
101
+ // Store token in multiple ways
102
+ var input = document.createElement('input');
103
+ input.type = 'hidden';
104
+ input.name = 'g-recaptcha-response';
105
+ input.value = token;
106
+ input.id = 'recaptcha-token-input';
107
+ document.body.appendChild(input);
108
+
109
+ localStorage.setItem('recaptcha_token', token);
110
+ };
111
+
112
+ window.recaptchaExpired = function() {
113
+ console.log('reCAPTCHA expired');
114
+ window.recaptchaToken = null;
115
+ window.recaptchaSolved = false;
116
+ document.getElementById('status').innerHTML = '❌ reCAPTCHA Expired - Refreshing...';
117
+ document.getElementById('status').style.background = '#fff3cd';
118
+ document.getElementById('status').style.color = '#856404';
119
+
120
+ var existing = document.getElementById('recaptcha-token-input');
121
+ if (existing) existing.remove();
122
+
123
+ // Auto-refresh after expiration
124
+ setTimeout(() => {
125
+ if (window.grecaptcha) {
126
+ grecaptcha.reset();
127
+ }
128
+ }, 1000);
129
+ };
130
+
131
+ window.recaptchaError = function() {
132
+ console.log('reCAPTCHA error');
133
+ document.getElementById('status').innerHTML = '❌ reCAPTCHA Error';
134
+ document.getElementById('status').style.background = '#f8d7da';
135
+ document.getElementById('status').style.color = '#721c24';
136
+ };
137
+
138
+ window.executeInvisible = function() {
139
+ if (window.grecaptcha) {
140
+ grecaptcha.execute();
141
+ }
142
+ };
143
+
144
+ window.checkToken = function() {
145
+ const token = window.recaptchaToken || document.getElementById('recaptcha-token-input')?.value;
146
+ if (token) {
147
+ document.getElementById('status').innerHTML = 'Token: ' + token;
148
+ } else {
149
+ document.getElementById('status').innerHTML = 'No token yet';
150
+ }
151
+ };
152
+
153
+ // Auto-execute for invisible reCAPTCHA
154
+ window.onload = function() {
155
+ setTimeout(function() {
156
+ // For invisible reCAPTCHA, auto-execute
157
+ if (${isInvisible} && window.grecaptcha) {
158
+ grecaptcha.execute();
159
+ }
160
+
161
+ // For visible reCAPTCHA, try to find and click
162
+ if (!${isInvisible}) {
163
+ var iframe = document.querySelector('iframe[src*="recaptcha"]');
164
+ if (iframe) {
165
+ console.log('Attempting to interact with reCAPTCHA');
166
+ var rect = iframe.getBoundingClientRect();
167
+ var clickEvent = new MouseEvent('click', {
168
+ view: window,
169
+ bubbles: true,
170
+ cancelable: true,
171
+ clientX: rect.left + rect.width / 2,
172
+ clientY: rect.top + rect.height / 2
173
+ });
174
+ iframe.dispatchEvent(clickEvent);
175
+ }
176
+ }
177
+ }, 2000);
178
+ };
179
+ </script>
180
+
181
+ <!-- Load reCAPTCHA API -->
182
+ <script src="https://www.google.com/recaptcha/api.js" async defer></script>
183
+ </body>
184
+ </html>
185
+ `;
186
+
187
+ // Setup request interception
188
+ await page.setRequestInterception(true);
189
+ page.removeAllListeners("request");
190
+
191
+ page.on("request", async (request) => {
192
+ const url = request.url();
193
+
194
+ // Handle main document request
195
+ if ([domain, domain + "/"].includes(url) && request.resourceType() === "document") {
196
+ await request.respond({
197
+ status: 200,
198
+ contentType: "text/html",
199
+ body: htmlContent,
200
+ });
201
+ }
202
+ // Block unnecessary resources untuk mempercepat
203
+ else if (request.resourceType() === 'image' ||
204
+ request.resourceType() === 'stylesheet' ||
205
+ request.resourceType() === 'font') {
206
+ await request.abort();
207
+ }
208
+ else {
209
+ await request.continue();
210
+ }
211
+ });
212
+
213
+ // Navigate ke page
214
+ await page.goto(domain, {
215
+ waitUntil: "domcontentloaded",
216
+ timeout: timeout
217
+ });
218
+
219
+ // Tunggu reCAPTCHA container load
220
+ await page.waitForSelector('.g-recaptcha', { timeout: 10000 });
221
+
222
+ // Tunggu token tersedia dengan multiple strategies
223
+ const token = await page.waitForFunction(() => {
224
+ // Cek dari hidden input
225
+ const input = document.querySelector('#recaptcha-token-input');
226
+ if (input && input.value && input.value.length > 10) {
227
+ return input.value;
228
+ }
229
+
230
+ // Cek dari localStorage
231
+ const stored = localStorage.getItem('recaptcha_token');
232
+ if (stored && stored.length > 10) {
233
+ return stored;
234
+ }
235
+
236
+ // Cek dari global variable
237
+ if (window.recaptchaToken && window.recaptchaToken.length > 10) {
238
+ return window.recaptchaToken;
239
+ }
240
+
241
+ return null;
242
+ }, { timeout, polling: 100 });
243
+
244
+ const tokenValue = await token.jsonValue();
245
+
246
+ isResolved = true;
247
+ clearTimeout(cl);
248
+
249
+ if (!tokenValue || tokenValue.length < 10) {
250
+ throw new Error("Failed to get valid reCAPTCHA token");
251
+ }
252
+
253
+ console.log('Successfully obtained reCAPTCHA token');
254
+ return { token: tokenValue, type: 'recaptcha_v2' };
255
+
256
+ } catch (error) {
257
+ clearTimeout(cl);
258
+
259
+ // Fallback: coba ambil token dengan method lain
260
+ try {
261
+ const fallbackToken = await page.evaluate(() => {
262
+ const input = document.querySelector('#recaptcha-token-input');
263
+ return input ? input.value : null;
264
+ });
265
+
266
+ if (fallbackToken && fallbackToken.length > 10) {
267
+ return { token: fallbackToken, type: 'recaptcha_v2' };
268
+ }
269
+ } catch (e) {
270
+ // Ignore fallback error
271
+ }
272
+
273
+ throw new Error(`reCAPTCHA solving failed: ${error.message}`);
274
+ }
275
+ }
276
+
277
+ module.exports = recaptchaV2;
index.js CHANGED
@@ -5,8 +5,8 @@ const path = require('path');
5
 
6
  const app = express();
7
  const port = process.env.PORT || 7860;
8
- const authToken = process.env.authToken || null;
9
- const domain = process.env.DOMAIN || `https://rezaharis-cf.hf.space`; // VARIABEL DOMAIN DI SINI
10
 
11
  global.browserLimit = Number(process.env.browserLimit) || 20;
12
  global.timeOut = Number(process.env.timeOut) || 60000;
@@ -56,11 +56,13 @@ app.get("/", (req, res) => {
56
  domain: domain,
57
  endpoints: {
58
  cloudflare: `${domain}/cloudflare`,
59
- turnstile: `${domain}/cloudflare` // via POST ke /cloudflare dengan mode turnstile
 
60
  },
61
  status: {
62
  browserLimit: global.browserLimit,
63
- timeOut: global.timeOut
 
64
  }
65
  });
66
  });
@@ -69,6 +71,7 @@ if (process.env.NODE_ENV !== 'development') {
69
  let server = app.listen(port, () => {
70
  console.log(`Server running on port ${port}`);
71
  console.log(`Domain: ${domain}`);
 
72
  });
73
  try {
74
  server.timeout = global.timeOut;
@@ -100,19 +103,22 @@ async function createBrowser(proxyServer = null) {
100
 
101
  const turnstile = require('./endpoints/turnstile');
102
  const cloudflare = require('./endpoints/cloudflare');
 
103
 
104
  app.post('/cloudflare', async (req, res) => {
105
  const data = req.body;
106
  if (!data || typeof data.mode !== 'string')
107
  return res.status(400).json({ message: 'Bad Request: missing or invalid mode' });
108
- if (authToken && data.authToken !== authToken)
109
- return res.status(401).json({ message: 'Unauthorized' });
 
 
110
 
111
  if (global.browserLimit <= 0)
112
  return res.status(429).json({ message: 'Too Many Requests' });
113
 
114
  let cacheKey, cached;
115
- if (data.mode === "iuam") {
116
  cacheKey = JSON.stringify(data);
117
  cached = readCache(cacheKey);
118
  if (cached) return res.status(200).json({ ...cached, cached: true });
@@ -143,6 +149,13 @@ app.post('/cloudflare', async (req, res) => {
143
  if (!result.code || result.code === 200) writeCache(cacheKey, result);
144
  break;
145
 
 
 
 
 
 
 
 
146
  default:
147
  result = { code: 400, message: 'Invalid mode' };
148
  }
 
5
 
6
  const app = express();
7
  const port = process.env.PORT || 7860;
8
+ const authToken = process.env.authToken || null; // Set null biar gak perlu auth
9
+ const domain = process.env.DOMAIN || `https://rezaharis-cf.hf.space`;
10
 
11
  global.browserLimit = Number(process.env.browserLimit) || 20;
12
  global.timeOut = Number(process.env.timeOut) || 60000;
 
56
  domain: domain,
57
  endpoints: {
58
  cloudflare: `${domain}/cloudflare`,
59
+ turnstile: `${domain}/cloudflare`,
60
+ recaptcha: `${domain}/cloudflare`
61
  },
62
  status: {
63
  browserLimit: global.browserLimit,
64
+ timeOut: global.timeOut,
65
+ authRequired: authToken !== null
66
  }
67
  });
68
  });
 
71
  let server = app.listen(port, () => {
72
  console.log(`Server running on port ${port}`);
73
  console.log(`Domain: ${domain}`);
74
+ console.log(`Auth required: ${authToken !== null}`);
75
  });
76
  try {
77
  server.timeout = global.timeOut;
 
103
 
104
  const turnstile = require('./endpoints/turnstile');
105
  const cloudflare = require('./endpoints/cloudflare');
106
+ const recaptchaV2 = require('./endpoints/recaptchav2');
107
 
108
  app.post('/cloudflare', async (req, res) => {
109
  const data = req.body;
110
  if (!data || typeof data.mode !== 'string')
111
  return res.status(400).json({ message: 'Bad Request: missing or invalid mode' });
112
+
113
+ // COMMENT/HAPUS pengecekan auth token biar gak perlu auth
114
+ // if (authToken && data.authToken !== authToken)
115
+ // return res.status(401).json({ message: 'Unauthorized' });
116
 
117
  if (global.browserLimit <= 0)
118
  return res.status(429).json({ message: 'Too Many Requests' });
119
 
120
  let cacheKey, cached;
121
+ if (data.mode === "iuam" || data.mode === "recaptcha") {
122
  cacheKey = JSON.stringify(data);
123
  cached = readCache(cacheKey);
124
  if (cached) return res.status(200).json({ ...cached, cached: true });
 
149
  if (!result.code || result.code === 200) writeCache(cacheKey, result);
150
  break;
151
 
152
+ case "recaptcha":
153
+ result = await recaptchaV2(data, page)
154
+ .then(r => ({ ...r }))
155
+ .catch(err => ({ code: 500, message: err.message }));
156
+ if (!result.code || result.code === 200) writeCache(cacheKey, result);
157
+ break;
158
+
159
  default:
160
  result = { code: 400, message: 'Invalid mode' };
161
  }