* Checks for CAPTCHA verification and solves the CAPTCHA if needed * @returns {string|null} hCaptcha token. If no verification is required, returns null
()
| 305 | * @returns {string|null} hCaptcha token. If no verification is required, returns null |
| 306 | */ |
| 307 | public async getCaptcha(): Promise<string|null> { |
| 308 | if (!await this.captchaRequired()) |
| 309 | return null; |
| 310 | |
| 311 | logger.info('CAPTCHA required. Launching browser...') |
| 312 | const browser = await this.launchBrowser(); |
| 313 | const page = await browser.newPage(); |
| 314 | await page.goto('https://suno.com/create', { referer: 'https://www.google.com/', waitUntil: 'domcontentloaded', timeout: 0 }); |
| 315 | |
| 316 | logger.info('Waiting for Suno interface to load'); |
| 317 | // await page.locator('.react-aria-GridList').waitFor({ timeout: 60000 }); |
| 318 | await page.waitForResponse('**/api/project/**\\?**', { timeout: 60000 }); // wait for song list API call |
| 319 | |
| 320 | if (this.ghostCursorEnabled) |
| 321 | this.cursor = await createCursor(page); |
| 322 | |
| 323 | logger.info('Triggering the CAPTCHA'); |
| 324 | try { |
| 325 | await page.getByLabel('Close').click({ timeout: 2000 }); // close all popups |
| 326 | // await this.click(page, { x: 318, y: 13 }); |
| 327 | } catch(e) {} |
| 328 | |
| 329 | const textarea = page.locator('.custom-textarea'); |
| 330 | await this.click(textarea); |
| 331 | await textarea.pressSequentially('Lorem ipsum', { delay: 80 }); |
| 332 | |
| 333 | const button = page.locator('button[aria-label="Create"]').locator('div.flex'); |
| 334 | this.click(button); |
| 335 | |
| 336 | const controller = new AbortController(); |
| 337 | new Promise<void>(async (resolve, reject) => { |
| 338 | const frame = page.frameLocator('iframe[title*="hCaptcha"]'); |
| 339 | const challenge = frame.locator('.challenge-container'); |
| 340 | try { |
| 341 | let wait = true; |
| 342 | while (true) { |
| 343 | if (wait) |
| 344 | await waitForRequests(page, controller.signal); |
| 345 | const drag = (await challenge.locator('.prompt-text').first().innerText()).toLowerCase().includes('drag'); |
| 346 | let captcha: any; |
| 347 | for (let j = 0; j < 3; j++) { // try several times because sometimes 2Captcha could return an error |
| 348 | try { |
| 349 | logger.info('Sending the CAPTCHA to 2Captcha'); |
| 350 | const payload: paramsCoordinates = { |
| 351 | body: (await challenge.screenshot({ timeout: 5000 })).toString('base64'), |
| 352 | lang: process.env.BROWSER_LOCALE |
| 353 | }; |
| 354 | if (drag) { |
| 355 | // Say to the worker that he needs to click |
| 356 | payload.textinstructions = 'CLICK on the shapes at their edge or center as shown above—please be precise!'; |
| 357 | payload.imginstructions = (await fs.readFile(path.join(process.cwd(), 'public', 'drag-instructions.jpg'))).toString('base64'); |
| 358 | } |
| 359 | captcha = await this.solver.coordinates(payload); |
| 360 | break; |
| 361 | } catch(err: any) { |
| 362 | logger.info(err.message); |
| 363 | if (j != 2) |
| 364 | logger.info('Retrying...'); |
no test coverage detected