index.js
function onCaptcha (options, response, body) {
  const callback = options.callback;
  // UDF that has the responsibility of returning control back to cloudscraper
  const handler = options.onCaptcha;
  // The form data to send back to Cloudflare
  const payload = { /* s, g-re-captcha-response */ };

  let cause;
  let match;

  match = body.match(/<form(?: [^<>]*)? id=["']?challenge-form['"]?(?: [^<>]*)?>([\S\s]*?)<\/form>/);
  if (!match) {
    cause = 'Challenge form extraction failed';
    return callback(new ParserError(cause, options, response));
  }

  // Defining response.challengeForm for debugging purposes
  const form = response.challengeForm = match[1];

  match = form.match(/\/recaptcha\/api\/fallback\?k=([^\s"'<>]*)/);
  if (!match) {
    // The site key wasn't inside the form so search the entire document
    match = body.match(/data-sitekey=["']?([^\s"'<>]*)/);
    if (!match) {
      cause = 'Unable to find the reCAPTCHA site key';
      return callback(new ParserError(cause, options, response));
    }
  }

  // Everything that is needed to solve the reCAPTCHA
  response.captcha = {
    url: response.request.uri.href,
    siteKey: match[1],
    form: payload
  };

  // Adding formData
  match = form.match(/<input(?: [^<>]*)? name=[^<>]+>/g);
  if (!match) {
    cause = 'Challenge form is missing inputs';
    return callback(new ParserError(cause, options, response));
  }

  const inputs = match;
  // Only adding inputs that have both a name and value defined
  for (let name, value, i = 0; i < inputs.length; i++) {
    name = inputs[i].match(/name=["']?([^\s"'<>]*)/);
    if (name) {
      value = inputs[i].match(/value=["']?([^\s"'<>]*)/);
      if (value) {
        payload[name[1]] = value[1];
      }
    }
  }

  // Sanity check
  if (!payload['s']) {
    cause = 'Challenge form is missing secret input';
    return callback(new ParserError(cause, options, response));
  }

  // The callback used to green light form submission
  const submit = function (error) {
    if (error) {
      // Pass an user defined error back to the original request call
      return callback(new CaptchaError(error, options, response));
    }

    onSubmitCaptcha(options, response, body);
  };

  // This seems like an okay-ish API (fewer arguments to the handler)
  response.captcha.submit = submit;

  // We're handing control over to the user now.
  const thenable = handler(options, response, body);
  // Handle the case where the user returns a promise
  if (thenable && typeof thenable.then === 'function') {
    // eslint-disable-next-line promise/catch-or-return
    thenable.then(submit, function (error) {
      if (!error) {
        // The user broke their promise with a falsy error
        submit(new Error('Falsy error'));
      } else {
        submit(error);
      }
    });
  }
}
function onCaptcha (options, response, body) {
  const callback = options.callback;
  // UDF that has the responsibility of returning control back to cloudscraper
  const handler = options.onCaptcha;
  // The form data to send back to Cloudflare
  const payload = { /* s, g-re-captcha-response */ };

  let cause;
  let match;

  match = body.match(/<form(?: [^<>]*)? id=["']?challenge-form['"]?(?: [^<>]*)?>([\S\s]*?)<\/form>/);
  if (!match) {
    cause = 'Challenge form extraction failed';
    return callback(new ParserError(cause, options, response));
  }

  // Defining response.challengeForm for debugging purposes
  const form = response.challengeForm = match[1];

  match = form.match(/\/recaptcha\/api\/fallback\?k=([^\s"'<>]*)/);
  if (!match) {
    // The site key wasn't inside the form so search the entire document
    match = body.match(/data-sitekey=["']?([^\s"'<>]*)/);
    if (!match) {
      cause = 'Unable to find the reCAPTCHA site key';
      return callback(new ParserError(cause, options, response));
    }
  }

  // Everything that is needed to solve the reCAPTCHA
  response.captcha = {
    url: response.request.uri.href,
    siteKey: match[1],
    form: payload
  };

  // Adding formData
  match = form.match(/<input(?: [^<>]*)? name=[^<>]+>/g);
  if (!match) {
    cause = 'Challenge form is missing inputs';
    return callback(new ParserError(cause, options, response));
  }

  const inputs = match;
  // Only adding inputs that have both a name and value defined
  for (let name, value, i = 0; i < inputs.length; i++) {
    name = inputs[i].match(/name=["']?([^\s"'<>]*)/);
    if (name) {
      value = inputs[i].match(/value=["']?([^\s"'<>]*)/);
      if (value) {
        payload[name[1]] = value[1];
      }
    }
  }

  // Sanity check
  if (!payload['s']) {
    cause = 'Challenge form is missing secret input';
    return callback(new ParserError(cause, options, response));
  }

  // The callback used to green light form submission
  const submit = function (error) {
    if (error) {
      // Pass an user defined error back to the original request call
      return callback(new CaptchaError(error, options, response));
    }

    onSubmitCaptcha(options, response, body);
  };

  // This seems like an okay-ish API (fewer arguments to the handler)
  response.captcha.submit = submit;

  // We're handing control over to the user now.
  const thenable = handler(options, response, body);
  // Handle the case where the user returns a promise
  if (thenable && typeof thenable.then === 'function') {
    // eslint-disable-next-line promise/catch-or-return
    thenable.then(submit, function (error) {
      if (!error) {
        // The user broke their promise with a falsy error
        submit(new Error('Falsy error'));
      } else {
        submit(error);
      }
    });
  }
}
function onCaptcha ( options , response , body ) { const callback = options . callback ; // UDF that has the responsibility of returning control back to cloudscraper const handler = options . onCaptcha ; // The form data to send back to Cloudflare const payload = { /* s, g-re-captcha-response */ } ; let cause ; let match ; match = body . match ( / <form(?: [^<>]*)? id=["']?challenge-form['"]?(?: [^<>]*)?>([\S\s]*?)<\/form> / ) ; if ( ! match ) { cause = 'Challenge form extraction failed' ; return callback ( new ParserError ( cause , options , response ) ) ; } // Defining response.challengeForm for debugging purposes const form = response . challengeForm = match [ 1 ] ; match = form . match ( / \/recaptcha\/api\/fallback\?k=([^\s"'<>]*) / ) ; if ( ! match ) { // The site key wasn't inside the form so search the entire document match = body . match ( / data-sitekey=["']?([^\s"'<>]*) / ) ; if ( ! match ) { cause = 'Unable to find the reCAPTCHA site key' ; return callback ( new ParserError ( cause , options , response ) ) ; } } // Everything that is needed to solve the reCAPTCHA response . captcha = { url : response . request . uri . href , siteKey : match [ 1 ] , form : payload } ; // Adding formData match = form . match ( / <input(?: [^<>]*)? name=[^<>]+> / g ) ; if ( ! match ) { cause = 'Challenge form is missing inputs' ; return callback ( new ParserError ( cause , options , response ) ) ; } const inputs = match ; // Only adding inputs that have both a name and value defined for ( let name , value , i = 0 ; i < inputs . length ; i ++ ) { name = inputs [ i ] . match ( / name=["']?([^\s"'<>]*) / ) ; if ( name ) { value = inputs [ i ] . match ( / value=["']?([^\s"'<>]*) / ) ; if ( value ) { payload [ name [ 1 ] ] = value [ 1 ] ; } } } // Sanity check if ( ! payload [ 's' ] ) { cause = 'Challenge form is missing secret input' ; return callback ( new ParserError ( cause , options , response ) ) ; } // The callback used to green light form submission const submit = function ( error ) { if ( error ) { // Pass an user defined error back to the original request call return callback ( new CaptchaError ( error , options , response ) ) ; } onSubmitCaptcha ( options , response , body ) ; } ; // This seems like an okay-ish API (fewer arguments to the handler) response . captcha . submit = submit ; // We're handing control over to the user now. const thenable = handler ( options , response , body ) ; // Handle the case where the user returns a promise if ( thenable && typeof thenable . then === 'function' ) { // eslint-disable-next-line promise/catch-or-return thenable . then ( submit , function ( error ) { if ( ! error ) { // The user broke their promise with a falsy error submit ( new Error ( 'Falsy error' ) ) ; } else { submit ( error ) ; } } ) ; } }
Parses the reCAPTCHA form and hands control over to the user
Parses the reCAPTCHA form and hands control over to the user