File size: 9,229 Bytes
10bc490 |
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 |
async function recaptchaV2({ domain, proxy, siteKey, action = "submit", isInvisible = false }, page) {
if (!domain) throw new Error("Missing domain parameter");
if (!siteKey) throw new Error("Missing siteKey parameter");
const timeout = global.timeOut || 60000;
let isResolved = false;
const cl = setTimeout(async () => {
if (!isResolved) {
throw new Error("Timeout Error");
}
}, timeout);
try {
if (proxy?.username && proxy?.password) {
await page.authenticate({
username: proxy.username,
password: proxy.password,
});
}
const htmlContent = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>reCAPTCHA v2 Solver</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 20px;
background: #f5f5f5;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
.container {
background: white;
padding: 30px;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
text-align: center;
}
.status {
margin-top: 20px;
padding: 10px;
border-radius: 5px;
background: #f8f9fa;
}
button {
background: #007bff;
color: white;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
margin: 10px;
}
button:hover {
background: #0056b3;
}
</style>
</head>
<body>
<div class="container">
<h2>reCAPTCHA v2 Solver</h2>
<p>SiteKey: ${siteKey}</p>
<div id="recaptcha-container">
<div class="g-recaptcha"
data-sitekey="${siteKey}"
data-callback="recaptchaCallback"
data-expired-callback="recaptchaExpired"
data-error-callback="recaptchaError"
data-size="${isInvisible ? 'invisible' : 'normal'}"
data-theme="light">
</div>
</div>
${isInvisible ? '<button onclick="executeInvisible()">Execute reCAPTCHA</button>' : ''}
<button onclick="checkToken()">Check Token</button>
<div class="status" id="status">Waiting for reCAPTCHA...</div>
</div>
<script>
// Global variables
window.recaptchaToken = null;
window.recaptchaSolved = false;
// Callback functions
window.recaptchaCallback = function(token) {
console.log('reCAPTCHA token received:', token);
window.recaptchaToken = token;
window.recaptchaSolved = true;
document.getElementById('status').innerHTML = '✅ reCAPTCHA Solved! Token: ' + token.substring(0, 20) + '...';
document.getElementById('status').style.background = '#d4edda';
document.getElementById('status').style.color = '#155724';
// Store token in multiple ways
var input = document.createElement('input');
input.type = 'hidden';
input.name = 'g-recaptcha-response';
input.value = token;
input.id = 'recaptcha-token-input';
document.body.appendChild(input);
localStorage.setItem('recaptcha_token', token);
};
window.recaptchaExpired = function() {
console.log('reCAPTCHA expired');
window.recaptchaToken = null;
window.recaptchaSolved = false;
document.getElementById('status').innerHTML = '❌ reCAPTCHA Expired - Refreshing...';
document.getElementById('status').style.background = '#fff3cd';
document.getElementById('status').style.color = '#856404';
var existing = document.getElementById('recaptcha-token-input');
if (existing) existing.remove();
// Auto-refresh after expiration
setTimeout(() => {
if (window.grecaptcha) {
grecaptcha.reset();
}
}, 1000);
};
window.recaptchaError = function() {
console.log('reCAPTCHA error');
document.getElementById('status').innerHTML = '❌ reCAPTCHA Error';
document.getElementById('status').style.background = '#f8d7da';
document.getElementById('status').style.color = '#721c24';
};
window.executeInvisible = function() {
if (window.grecaptcha) {
grecaptcha.execute();
}
};
window.checkToken = function() {
const token = window.recaptchaToken || document.getElementById('recaptcha-token-input')?.value;
if (token) {
document.getElementById('status').innerHTML = 'Token: ' + token;
} else {
document.getElementById('status').innerHTML = 'No token yet';
}
};
// Auto-execute for invisible reCAPTCHA
window.onload = function() {
setTimeout(function() {
// For invisible reCAPTCHA, auto-execute
if (${isInvisible} && window.grecaptcha) {
grecaptcha.execute();
}
// For visible reCAPTCHA, try to find and click
if (!${isInvisible}) {
var iframe = document.querySelector('iframe[src*="recaptcha"]');
if (iframe) {
console.log('Attempting to interact with reCAPTCHA');
var rect = iframe.getBoundingClientRect();
var clickEvent = new MouseEvent('click', {
view: window,
bubbles: true,
cancelable: true,
clientX: rect.left + rect.width / 2,
clientY: rect.top + rect.height / 2
});
iframe.dispatchEvent(clickEvent);
}
}
}, 2000);
};
</script>
<!-- Load reCAPTCHA API -->
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
</body>
</html>
`;
// Setup request interception
await page.setRequestInterception(true);
page.removeAllListeners("request");
page.on("request", async (request) => {
const url = request.url();
// Handle main document request
if ([domain, domain + "/"].includes(url) && request.resourceType() === "document") {
await request.respond({
status: 200,
contentType: "text/html",
body: htmlContent,
});
}
// Block unnecessary resources untuk mempercepat
else if (request.resourceType() === 'image' ||
request.resourceType() === 'stylesheet' ||
request.resourceType() === 'font') {
await request.abort();
}
else {
await request.continue();
}
});
// Navigate ke page
await page.goto(domain, {
waitUntil: "domcontentloaded",
timeout: timeout
});
// Tunggu reCAPTCHA container load
await page.waitForSelector('.g-recaptcha', { timeout: 10000 });
// Tunggu token tersedia dengan multiple strategies
const token = await page.waitForFunction(() => {
// Cek dari hidden input
const input = document.querySelector('#recaptcha-token-input');
if (input && input.value && input.value.length > 10) {
return input.value;
}
// Cek dari localStorage
const stored = localStorage.getItem('recaptcha_token');
if (stored && stored.length > 10) {
return stored;
}
// Cek dari global variable
if (window.recaptchaToken && window.recaptchaToken.length > 10) {
return window.recaptchaToken;
}
return null;
}, { timeout, polling: 100 });
const tokenValue = await token.jsonValue();
isResolved = true;
clearTimeout(cl);
if (!tokenValue || tokenValue.length < 10) {
throw new Error("Failed to get valid reCAPTCHA token");
}
console.log('Successfully obtained reCAPTCHA token');
return { token: tokenValue, type: 'recaptcha_v2' };
} catch (error) {
clearTimeout(cl);
// Fallback: coba ambil token dengan method lain
try {
const fallbackToken = await page.evaluate(() => {
const input = document.querySelector('#recaptcha-token-input');
return input ? input.value : null;
});
if (fallbackToken && fallbackToken.length > 10) {
return { token: fallbackToken, type: 'recaptcha_v2' };
}
} catch (e) {
// Ignore fallback error
}
throw new Error(`reCAPTCHA solving failed: ${error.message}`);
}
}
module.exports = recaptchaV2; |