Node.js में Capy Puzzle कैसे हल करें और session कैसे बनाए रखें

solve capy puzzle in node.js - How to Solve Capy Puzzle in Node.js and Keep the Session

Node.js में Capy Puzzle हल करने के लिए, पेज fetch करें और widget script से PUZZLE_ key पढ़ें। उसे पेज URL के साथ capskip पैकेज के capy method को पास करें। फिर वह जो तीन वैल्यू लौटाता है, उन्हें capy_captchakey, capy_challengekey और capy_answer फ़ील्ड में एक साथ और तुरंत पोस्ट करें, उसी session के हिस्से के रूप में जिससे पेज लाया गया था। Node कोड आमतौर पर इसी आख़िरी हिस्से पर टूटता है। Node का built-in fetch न कोई cookie रखता है, न कोई Referer भेजता है। तब फॉर्म पोस्ट उस session के बिना पहुँचती है जो पेज ने खोला था, और साइट उसे ठुकरा देती है, जवाब चाहे कितना भी सही हो। CapSkip ने Capy Puzzle को वर्ज़न 1.4.0 में जोड़ा। यह गाइड key, कॉल, दो सेकंड का ठहराव, सबमिट, दोबारा कोशिशें और एक साथ कई लॉगिन चलाना कवर करती है।

आपको क्या चाहिए

  • किसी Windows मशीन पर चल रहा CapSkip 1.4.0 या उसके बाद का संस्करण। Capy Puzzle उसी रिलीज़ में जोड़ा गया था, CaptchaFox और Friendly Captcha के साथ।
  • Node.js 20.18.1 या उसके बाद का संस्करण, जो cheerio की सपोर्ट की हुई सबसे पुरानी रिलीज़ है। नए प्रोजेक्ट पर 22 या 24 इस्तेमाल करें, क्योंकि Node 20 अप्रैल 2026 में end of life पर पहुँच चुका है।
  • capskip पैकेज का 1.3.0 या उसके बाद का वर्ज़न, जो capy method वाली पहली रिलीज़ है, और साथ में फॉर्म पढ़ने के लिए cheerio।
  • उस पेज का URL जो पज़ल दिखाता है। key, Capy host और फॉर्म फ़ील्ड, सब उसी पेज से मिलते हैं।
  • सॉल्वर के लिए एक पता। Local मोड में CapSkip केवल उसी डिवाइस के लिए 127.0.0.1 पर जवाब देता है; Server मोड में वह आपके नेटवर्क पते या पब्लिक IP पर सुनता है, ताकि किसी दूसरी मशीन पर चल रही script API के ज़रिए उसे कॉल कर सके। दोनों यहाँ सेट किए जाते हैं: कनेक्शन सेटिंग्स.
# capskip 1.3.0 added capy(); cheerio reads the form
npm install capskip cheerio

सैंपल top-level await वाले ES modules हैं। उन्हें .mjs extension के साथ सेव करें, या अपने package.json में type फ़ील्ड को module पर सेट करें, क्योंकि npm init के हाल के वर्ज़न वहाँ commonjs लिखते हैं, और जिस package.json में type फ़ील्ड ही न हो, वह भी CommonJS माना जाता है। अगर आपका प्रोजेक्ट CommonJS है, तो capskip पैकेज require से भी लोड हो जाता है।

स्टेप 1: पेज fetch करें, उसकी cookies रखें और key खोजें

Capy फॉर्म आमतौर पर किसी लॉगिन या साइन-अप पेज पर होते हैं, जो एक session cookie सेट करता है और फॉर्म में एक CSRF token रखता है। Node का fetch कॉल के बीच कोई cookie नहीं रखता, इसलिए पोस्ट करने तक वह cookie गायब हो चुकी होती है, और token फिर किसी चीज़ से मेल नहीं खाता। cookies को खुद आगे ले जाएँ। Headers.getSetCookie() हर Set-Cookie लाइन को अलग-अलग लौटाता है, और नीचे वाले छोटे addCookies helper को बस इतना ही चाहिए; MDN में इसका दस्तावेज़ यहाँ है: Headers.getSetCookie().

// npm install capskip cheerio
import * as cheerio from "cheerio";

const PAGE_URL = "https://example.com/login";

// fetch() keeps no cookies between calls, so carry them yourself.
const jar = new Map();
function addCookies(res) {
  for (const line of res.headers.getSetCookie()) {
    const pair = line.split(";")[0];
    const eq = pair.indexOf("=");
    if (eq > 0) jar.set(pair.slice(0, eq).trim(), pair.slice(eq + 1).trim());
  }
}

const page = await fetch(PAGE_URL);
addCookies(page);
const html = await page.text();
const $ = cheerio.load(html);

// The widget script's k= parameter, or a capy_captchakey input.
const key = html.match(/PUZZLE_[A-Za-z0-9_-]+/)?.[0];
// Everything before /puzzle/get_js/ is the Capy host.
const apiServer = html.match(/(https:\/\/[^"'\s<>]+?)\/puzzle\/get_js\//)?.[1];
// The form that holds the widget script or a capy_captchakey input.
const form = $("form")
  .has('script[src*="/puzzle/get_js/"], input[name="capy_captchakey"]').first();
console.log(key, apiServer ?? "default host", form.length);

key सार्वजनिक होती है और हर विज़िटर के लिए एक जैसी होती है, और पज़ल की key PUZZLE_ से शुरू होती है। ज़्यादातर पेज इसे widget script के URL के k पैरामीटर में रखते हैं, जो उस HTML में होता है जिसे fetch डाउनलोड करता है। कुछ पेज फॉर्म में एक hidden capy_captchakey input भी रेंडर करते हैं, और ब्राउज़र में widget चल जाने के बाद यह input हमेशा मौजूद होता है, और DevTools में key वहीं दिखती है। एक ही regular expression key को दोनों में से किसी भी जगह ढूँढ लेता है।

वहाँ रहते हुए script का host भी नोट कर लें। script URL में /puzzle/get_js/ से पहले का पूरा हिस्सा वह Capy API है जिसके पीछे key रहती है, और CapSkip उसे api_server कहता है। इसका डिफ़ॉल्ट https://jp.api.capy.me है, जहाँ लाइव सर्विस चलती है, इसलिए इसकी ज़रूरत सिर्फ़ तब है जब कोई पेज widget को कहीं और से लोड करे। दूसरे सॉल्वरों के लिए लिखे गए सैंपल अक्सर क्षेत्रीय prefix के बिना api.capy.me दिखाते हैं, और वह host अब resolve नहीं होता, इसलिए अगर आपने ऐसा कोई सैंपल कॉपी किया है, तो उसमें से यह host हटा दें। regex एक https:// URL की उम्मीद करता है; अगर src इसके बजाय // से शुरू होता है, तो host पास करने से पहले उसके आगे https: लगा दें।

form वाली लाइन वह फॉर्म चुनती है जिसमें widget script या कोई capy_captchakey input हो। जो पेज script को अपने head में या फॉर्म के नीचे लोड करता है, और फॉर्म के भीतर कोई capy_captchakey input नहीं रखता, वहाँ form.length 0 रहता है। ऐसे पेज पर फॉर्म को इसके बजाय उसकी id या उसके action से चुनें।

स्टेप 2: solver.capy() कॉल

Node.js में Capy Puzzle हल करने के लिए आपको एक ही method चाहिए। यह key, पेज URL और एक वैकल्पिक options object लेता है, और डिफ़ॉल्ट host वाले पेज के लिए उसे बस पहले दो की ज़रूरत होती है।

// npm install capskip
import { CapSkip } from "capskip";

const solver = new CapSkip({
  host: "127.0.0.1",
  port: 8080,
  // Capy answers after a two second hold; poll often enough to catch it.
  pollingInterval: 0.5,
});

// undefined (no widget script in the HTML) is dropped, so CapSkip
// uses its default host.
const result = await solver.capy(key, PAGE_URL, { apiServer });

console.log(result.captchakey);     // goes in capy_captchakey
console.log(result.challengekey);   // goes in capy_challengekey
console.log(result.answer);         // goes in capy_answer

नतीजा एक सादा object है। नाम वाले तीनों फ़ील्ड पढ़ें। result.code में सर्वर का पूरा जवाब होता है, चारों फ़ील्ड वाला एक object, जो logging के लिए काम आता है, और result.respKey एक खाली स्ट्रिंग है जो सिर्फ़ दूसरी सर्विसों के साथ compatibility के लिए मौजूद है। answer एक लंबी स्ट्रिंग है जो कुछ-कुछ 0xax8ex0xax84x जैसी शुरू होती है: यह वह ड्रैग पाथ है जिसे widget टुकड़ा खिसकते समय रिकॉर्ड करता, कोई coordinate नहीं।

method जिन ऑप्शन को स्वीकार करता है

apiServer, api_server का alias है, और यह method हर वह ऑप्शन हटा देता है जिसकी वैल्यू undefined या null हो, इसीलिए ऊपर वाली कॉल हर बार regex का नतीजा पास कर सकती है। यह type और uri keys वाला एक proxy object भी लेता है, और useragent भी, जो उस इकलौती रिक्वेस्ट पर भेजा जाता है जो CapSkip पज़ल लाने के लिए करता है। एक version ऑप्शन भी है, लेकिन उसमें सिर्फ़ puzzle स्वीकार होता है। Capy का दूसरा परिवार, avatar, एक अलग endpoint के पीछे एक अलग challenge है, इसलिए SDK ऐसा जवाब लौटाने के बजाय, जिसे साइट ठुकरा देगी, उसे मना कर देता है।

हर कॉल के दो ऑप्शन लोगों को उलझा देते हैं। सेकंड में दिया गया timeout उस एक solve के लिए क्लाइंट के defaultTimeout की जगह ले लेता है। पोलिंग की ऊपरी सीमा भी हर कॉल पर सेट की जा सकती है, लेकिन सिर्फ़ polling_interval के रूप में। camelCase वाला pollingInterval constructor का ऑप्शन है, और solver.capy() को पास करने पर वह ValidationException फेंकता है, जिसमें उस ऑप्शन का नाम होता है। TypeScript आपको चेतावनी नहीं देगा, क्योंकि options type कोई भी key स्वीकार करता है। खाली key या URL, कोई अनजान ऑप्शन, और HTTP, HTTPS, SOCKS5 या SOCKS5H के अलावा कोई proxy type भी ValidationException फेंकते हैं, और यह कोई भी रिक्वेस्ट आपकी मशीन से निकलने से पहले ही हो जाता है।

Capy solve में लगभग दो सेकंड क्यों लगते हैं

पहचान अपने आप में तेज़ है। CapSkip पज़ल की तस्वीर लाता है, थोड़े से pixel गणित से तस्वीर में कटा खाँचा ढूँढता है और ड्रैग पाथ बनाता है, और इसमें न कोई ब्राउज़र लगता है, न कोई मॉडल। फिर वह जानबूझकर इंतज़ार करता है।

Capy पज़ल जारी करने और जवाब मिलने के बीच का समय मापता है, और जो भी जवाब इतनी जल्दी आए कि कोई इंसान उतनी देर में टुकड़े को खींच ही न पाता, उसे ठुकरा देता है। Capy पर CapSkip के अपने टेस्ट के अनुसार यह निचली सीमा लगभग एक सेकंड है, और इनकार में वही संदेश आता है जो गलत जवाब पर आता है, इसलिए बहुत जल्दी पहुँचाया गया सही solve बिल्कुल खराब सॉल्वर जैसा दिखता है। इसलिए CapSkip हर Capy नतीजे को पज़ल लेने के दो सेकंड बाद तक रोके रखता है। यह इंतज़ार एक sleep है, इसलिए इसकी कीमत latency है, CPU नहीं।

डिफ़ॉल्ट सेटिंग्स में एक कॉल में चार सेकंड के क़रीब लगते हैं, और इसकी वजह polling है। job पहुँचते ही SDK नतीजा माँगता है, फिर चौथाई सेकंड बाद दोबारा। वह हर बार अंतर को दोगुना करता जाता है, उसके pollingInterval तक, जो डिफ़ॉल्ट रूप से 5 सेकंड है। poll 0, 0.25, 0.75, 1.75 और 3.75 सेकंड पर होते हैं, इसलिए दो सेकंड वाला जवाब पाँचवें poll पर उठाया जाता है। ऊपर की तरह constructor में pollingInterval: 0.5 रखने पर, हमारे टेस्ट में कॉल job भेजे जाने के लगभग 2.3 सेकंड बाद लौट आई। पोस्ट से पहले अपनी तरफ़ से कोई देरी न जोड़ें, और ठहराव को घटाने की कोशिश न करें: यही ठहराव जवाब को पास करवाता है।

स्टेप 3: तीनों वैल्यू पेज की cookies के साथ पोस्ट करें

तीनों वैल्यू उन्हीं फ़ील्ड में जाती हैं जिनमें widget उन्हें खुद फॉर्म में लिखता। उन्हें एक साथ, एक ही रिक्वेस्ट में भेजें, स्टेप 1 वाली cookies के साथ और पेज को Referer बनाकर। पोस्ट करते समय redirect को भी manual पर सेट करें, ताकि साइट जो session वापस देती है, वह आपके पास ही रहे।

// The form's own fields, hidden CSRF token included.
const body = new URLSearchParams(
  form.serializeArray().map((f) => [f.name, f.value]),
);
// Use the page's own field names, password included.
body.set("username", "YOUR_USERNAME");
body.set("capy_captchakey", result.captchakey);
body.set("capy_challengekey", result.challengekey);
body.set("capy_answer", result.answer);

const res = await fetch(new URL(form.attr("action") || PAGE_URL, PAGE_URL), {
  method: "POST",
  headers: {
    cookie: [...jar].map(([k, v]) => `${k}=${v}`).join("; "),
    // fetch sends no Referer; strict servers refuse a post without one.
    referer: PAGE_URL,
  },
  body,
  // A login answers 302 with a new session cookie; keep it.
  redirect: "manual",
});
addCookies(res);
console.log(res.status, res.headers.get("location"));

serializeArray वे फ़ील्ड इकट्ठा करता है जो ब्राउज़र भेजता, hidden CSRF token समेत, इसलिए आपको उनके नाम कभी नहीं लिखने पड़ते। यह submit बटन को छोड़ देता है, इसलिए अगर सर्वर बटन का नाम जाँचता है, तो उसे body.set से जोड़ें। body.set उस capy_captchakey input की जगह भी ले लेता है जिसे पेज ने खुद रेंडर किया था, ताकि key कभी दो बार न जाए। Referer उतना ही मायने रखता है जितनी cookie। fetch न कोई Referer भेजता है, न कोई Origin, और कुछ frameworks, जिनमें Django भी है, ऐसी HTTPS फॉर्म पोस्ट ठुकरा देते हैं जिसमें इनमें से कोई भी न हो, भले ही cookie, token और जवाब सही हों।

redirect सेटिंग कामयाब लॉगिन के बाद session को बचाए रखती है। लॉगिन आमतौर पर एक नई session cookie के साथ 302 का जवाब देता है, क्योंकि Django जैसे frameworks उसी पल session बदल देते हैं। अपने हाल पर छोड़ दिया जाए, तो fetch उस cookie के बिना redirect का पीछा करता है, वापस लॉगिन पेज पर पहुँचता है और 200 रिपोर्ट करता है। redirect को manual पर सेट करने से आपको खुद 302 मिलता है, और addCookies नई cookie को आगे की रिक्वेस्ट के लिए jar में रख देता है।

जवाब को ठीक वैसे ही पोस्ट करें जैसा वह लौटा है। साइट का backend इसे उस पज़ल से मिलाकर जाँचता है जो जारी हुआ था, इसलिए इसे काटना या दोबारा बनाना इसे अमान्य कर देता है। URLSearchParams बॉडी को percent-encode करता है, जो ठीक है, क्योंकि सर्वर जाँचने से पहले उसे decode करता है। फिर तुरंत पोस्ट करें। CapSkip हर solve के लिए एक नई challenge key बनाता है, और Capy जो पज़ल जारी करता है, उसे उसी key से बाँध देता है। इसलिए key एक ही बार इस्तेमाल होती है और कम समय तक टिकती है, और एक solve से एक सबमिशन होता है। कुछ पेज फॉर्म पोस्ट के बजाय अपनी script के ज़रिए JSON बॉडी के साथ सबमिट करते हैं। DevTools खोलकर एक बार हाथ से सबमिट करें, और तीनों वैल्यू वहीं रखें जहाँ पेज उन्हें रखता है।

स्टेप 4: solve फेल होने पर दोबारा कोशिश

जब आप Node.js में Capy Puzzle किसी भी मात्रा में हल करते हैं, तो कुछ solve फेल होंगे। फेल हुआ solve एक ApiException के रूप में आता है जिसके संदेश में ERROR_CAPTCHA_UNSOLVABLE होता है, और res.php के ज़रिए, जिस endpoint पर SDK poll करता है, यह एक ही code दो अलग-अलग स्थितियों को कवर करता है। खाँचा चूक जाना कभी-कभार होता है, और अगली कोशिश में किसी दूसरी फ़ोटो पर बिल्कुल नया पज़ल आता है, इसलिए वह आमतौर पर कामयाब होती है। जिस key को Capy API मना कर देता है, वह हर कोशिश पर फेल होती है, और CapSkip के Monitor टैब में Capy कार्ड पर Bad Keys की गिनती बढ़ जाती है। पहली स्थिति में दोबारा कोशिश करें और दूसरी को ठीक करें।

import { ApiException } from "capskip";

async function solveCapy(key, url, options = {}, attempts = 3) {
  for (let attempt = 1; ; attempt++) {
    try {
      return await solver.capy(key, url, options);
    } catch (err) {
      // A missed hole reads as unsolvable, and the next try draws a
      // new puzzle. Anything else, such as a wrong API key, is final.
      const miss = err instanceof ApiException && err.message.includes("UNSOLVABLE");
      if (!miss || attempt === attempts) throw err;
    }
  }
}

ApiException का संदेश CapSkip का कच्चा जवाब होता है, इसलिए जाँच error code को सीधे उसी से पढ़ती है। मना की गई key भी unsolvable के रूप में दिखती है, इसलिए यहाँ उस पर भी दोबारा कोशिश होती है, और वह हर कोशिश पर एक ही तरह फेल होती है, और यही उसकी पहचान है। दूसरा रास्ता CapSkip की सेटिंग्स के Capy सेक्शन में Retries सेटिंग है, जो 0 से 3 तक की वैल्यू लेती है और डिफ़ॉल्ट रूप से 0 है। तब CapSkip चूके हुए खाँचे पर खुद दोबारा कोशिश करता है, लेकिन मना की गई key पर कभी नहीं, क्योंकि वह उसी तरह फिर मना हो जाती।

capy method क्लाइंट के defaultTimeout पर poll करता है, जो 120 सेकंड है, क्योंकि Capy solve कोई ब्राउज़र session नहीं, बल्कि एक fetch और थोड़ा हिसाब भर है। CapSkip अपनी घड़ी भी चलाता है। उसकी Capy सेटिंग्स में कोई task 10 threads (Max. Threads) में से किसी एक के लिए 250 सेकंड (Wait Timeout) तक इंतज़ार कर सकता है, और एक कोशिश को 60 सेकंड (Row Timeout) मिलते हैं। एक सामान्य solve इन सभी सीमाओं में कई गुना आराम से समा जाता है। SDK connection errors के दौरान भी poll करता रहता है, इसलिए अगर CapSkip solve के बीच में रुक जाए, तो कॉल TimeoutException पर खत्म होती है, NetworkException पर नहीं।

पूरा चलने वाला उदाहरण

यह रहा पूरा run एक ही फ़ाइल में। logIn पेज को अपने cookie jar में fetch करता है, key और host पढ़ता है, दोबारा कोशिशों के साथ हल करता है और फॉर्म को उसकी cookies और एक Referer के साथ पोस्ट करता है। यह response और jar लौटाता है, ताकि लॉगिन के बाद की आपकी रिक्वेस्ट नया session साथ ले जाएँ।

// npm install capskip cheerio
// login.mjs: run with node login.mjs
import * as cheerio from "cheerio";
import {
  CapSkip, ApiException, CapSkipError, TimeoutException, ValidationException,
} from "capskip";

const PAGE_URL = "https://example.com/login";

const solver = new CapSkip({
  apiKey: process.env.CAPSKIP_API_KEY ?? "capskip",
  host: process.env.CAPSKIP_HOST ?? "127.0.0.1",
  port: Number(process.env.CAPSKIP_PORT ?? 8080),
  pollingInterval: 0.5,
});

async function solveCapy(key, url, options = {}, attempts = 3) {
  for (let attempt = 1; ; attempt++) {
    try {
      return await solver.capy(key, url, options);
    } catch (err) {
      // A missed hole reads as unsolvable; the next try draws a new puzzle.
      const miss = err instanceof ApiException && err.message.includes("UNSOLVABLE");
      if (!miss || attempt === attempts) throw err;
      console.log(`attempt ${attempt}: ${err.message}`);
    }
  }
}

function addCookies(jar, res) {
  for (const line of res.headers.getSetCookie()) {
    const pair = line.split(";")[0];
    const eq = pair.indexOf("=");
    if (eq > 0) jar.set(pair.slice(0, eq).trim(), pair.slice(eq + 1).trim());
  }
}

async function logIn(pageUrl, username) {
  // One jar per run, so runs side by side never share a session.
  const jar = new Map();
  const page = await fetch(pageUrl);
  addCookies(jar, page);
  const html = await page.text();
  const $ = cheerio.load(html);

  const key = html.match(/PUZZLE_[A-Za-z0-9_-]+/)?.[0];
  if (!key) throw new Error("No PUZZLE_ key in the HTML; find it in DevTools.");
  const apiServer = html.match(/(https:\/\/[^"'\s<>]+?)\/puzzle\/get_js\//)?.[1];
  const form = $("form")
    .has('script[src*="/puzzle/get_js/"], input[name="capy_captchakey"]').first();
  if (!form.length) throw new Error("No form holds the widget; select it by id.");

  const result = await solveCapy(key, pageUrl, { apiServer });

  // All three together, straight away: the challenge key is single-use.
  const body = new URLSearchParams(form.serializeArray().map((f) => [f.name, f.value]));
  // Use the page's own field names, password included.
  body.set("username", username);
  body.set("capy_captchakey", result.captchakey);
  body.set("capy_challengekey", result.challengekey);
  body.set("capy_answer", result.answer);

  const action = new URL(form.attr("action") || page.url, page.url);
  const res = await fetch(action, {
    method: "POST",
    headers: {
      cookie: [...jar].map(([k, v]) => `${k}=${v}`).join("; "),
      referer: page.url,
    },
    body,
    // A login answers 302 with a new session cookie. Keep the 302, so
    // its Set-Cookie lands in the jar instead of being followed away.
    redirect: "manual",
  });
  addCookies(jar, res);
  return { res, jar };
}

try {
  const { res, jar } = await logIn(PAGE_URL, "YOUR_USERNAME");
  // A 302 to the account page means it worked; jar holds that session.
  console.log(res.status, res.headers.get("location"), [...jar.keys()]);
} catch (err) {
  if (err instanceof ValidationException) console.error(`not sent: ${err.message}`);
  else if (err instanceof TimeoutException) console.error("gave up after defaultTimeout");
  else if (err instanceof CapSkipError) console.error(`solve failed: ${err.name}: ${err.message}`);
  else throw err;
  process.exitCode = 1;
}

अगर तीनों कोशिशें फेल हों, तो सबसे पहले key देखें, क्योंकि मना की गई key हर बार एक ही तरह फेल होती है, जबकि चूक लगभग कभी लगातार तीन बार नहीं होती। अगर key वाली जाँच error फेंकती है, तो पेज widget को किसी bundled script से बनाता है: Network टैब खोलें और key और host को widget की अपनी रिक्वेस्ट से कॉपी करें। इस method के पीछे का raw endpoint यहाँ दर्ज है: API रेफ़रेंस का Capy सेक्शन.

कई लॉगिन साथ-साथ चलाना

Node में CapSkip का हर method एक Promise लौटाता है, और AsyncCapSkip उसी class का बस दूसरा नाम है, इसलिए कई solve साथ-साथ चलाना सादा JavaScript है। आपको एक सीमा चाहिए। CapSkip डिफ़ॉल्ट रूप से एक साथ 10 Capy solve चलाता है और बाकी को खाली thread का इंतज़ार करवाता है, इसलिए एक छोटा पूल, जो एक समय में दस solve चालू रखे, उससे मेल खाता है। इसे पूरे उदाहरण के आख़िर में दिए try block की जगह रखें:

// At most `limit` calls in flight; a failure becomes that item's Error.
async function mapLimited(items, limit, fn) {
  const results = new Array(items.length);
  let next = 0;
  async function worker() {
    while (next < items.length) {
      const i = next++;
      results[i] = await fn(items[i]).catch((err) => err);
    }
  }
  await Promise.all(Array.from({ length: Math.min(limit, items.length) }, worker));
  return results;
}

const users = ["YOUR_USERNAME_1", "YOUR_USERNAME_2", "YOUR_USERNAME_3"];
const results = await mapLimited(users, 10, (user) => logIn(PAGE_URL, user));
for (const r of results) console.log(r instanceof Error ? r.message : r.res.status);

logIn हर कॉल पर अपना cookie jar बनाता है, और यही उसे parallel में सुरक्षित बनाता है। दस runs के बीच साझा एक jar उनके sessions और CSRF tokens को आपस में मिला देगा। यह हर solve को उसकी अपनी पोस्ट के साथ जोड़े भी रखता है। पहले सौ पज़ल हल करना और बाद में पोस्ट करना, सबसे पहली challenge keys को तब तक पुराना होने देता है जब तक आख़िरी solve पूरे होते हैं। बड़ी मात्रा में प्रॉक्सी भी जोड़ें। हर solve, Capy API से एक नया पज़ल लेता है, और एक ही पते से ऐसे पज़ल की लगातार धारा ठीक वही पैटर्न है जिसे पकड़ने के लिए rate limiting बनी है। CapSkip के Capy सेक्शन में एक प्रॉक्सी पूल कॉन्फ़िगर करें, या हर कॉल पर एक proxy object पास करें; यह सिर्फ़ पज़ल fetch को छूता है, जो Capy solve की इकलौती रिक्वेस्ट है।

सॉल्वर को किसी दूसरी मशीन पर चलाना

जब तक script और CapSkip एक ही Windows PC पर हों, 127.0.0.1 सही है। जैसे ही Node कोड किसी VPS, कंटेनर, CI runner या serverless function पर जाता है, loopback गलत मशीन की ओर इशारा करता है और पहला ही solve ECONNREFUSED के साथ NetworkException फेंकता है। CapSkip को Server मोड पर कर दें और वह आपके नेटवर्क पते या पब्लिक IP पर सुनने लगता है, ताकि इनमें से कोई भी उसी API के ज़रिए उसे कॉल कर सके। जब रास्ता इंटरनेट से होकर जाए तो स्टैटिक पब्लिक IP इस्तेमाल करें और API key validation चालू करें। Server मोड शुरू होते ही CapSkip अपना पोर्ट हर पते के लिए खोल देता है, इसलिए उसे सिर्फ़ अपेक्षित मशीनों तक सीमित रखने के लिए बाकी पतों के लिए Windows Firewall में एक block नियम जोड़ें। सॉल्वर अब भी आपकी अपनी Windows मशीन है, और solve अब भी बिना मीटर वाले हैं।

क्लाइंट एनवायरनमेंट वेरिएबल खुद नहीं पढ़ता। CAPSKIP_HOST, CAPSKIP_PORT और CAPSKIP_API_KEY को अपने कोड में पढ़ें और उन्हें constructor को पास करें, जैसा पूरा उदाहरण करता है, ताकि वही script आपकी डेस्क पर भी चले और सर्वर पर भी।

आम errors और उनका मतलब

आप जो देखते हैंकारणफिक्स
solver.capy() के तीनों वैल्यू लौटाने के बावजूद साइट कहती है कि session expire हो गयापोस्ट उन cookies के बिना गई जो पेज ने सेट की थीं, इसलिए CSRF फ़ील्ड अब किसी session से मेल नहीं खातापेज fetch वाली cookies आगे ले जाएँ, हर run के लिए एक jar
साइट के लॉगिन स्वीकार कर लेने के बावजूद पोस्ट 200 और लॉगिन पेज लौटाती हैलॉगिन ने नई session cookie के साथ 302 का जवाब दिया, और fetch ने उस cookie के बिना redirect का पीछा कियाredirect को manual पर सेट करके पोस्ट करें और 302 की Set-Cookie लाइनें jar में जोड़ें
सही cookie और तीनों वैल्यू होने पर भी पोस्ट पर 403fetch ने न कोई Referer भेजा, न कोई Origin, और साइट इन्हें जाँचती हैपेज URL को Referer header के रूप में भेजें
साइट जवाब को ही ठुकरा देती हैजवाब बदल दिया गया, या वैल्यू में से सिर्फ़ एक या दो ही फॉर्म तक पहुँचींresult.answer को हूबहू, बाकी दो वैल्यू के साथ, एक ही रिक्वेस्ट में पोस्ट करें
एक बार काम कर चुकी पोस्ट दूसरी बार फेल हो जाती हैchallenge key एक ही बार इस्तेमाल होती है और कम समय तक टिकती हैहर सबमिशन के लिए दोबारा हल करें, और तुरंत पोस्ट करें
एक ही key के लिए हर कोशिश पर एक ApiExceptionCapy API ने key मना कर दी, या api_server गलत host की ओर इशारा करता हैkey को prefix समेत दोबारा कॉपी करें, और script टैग का host जाँचें
कभी-कभार आने वाला ApiException, जिसमें ERROR_CAPTCHA_UNSOLVABLE होता हैCapSkip उस पज़ल में खाँचा नहीं ढूँढ पायादोबारा कोशिश करें; अगली कोशिश में दूसरा पज़ल आता है
कुछ भी भेजे जाने से पहले ही ValidationExceptionखाली key या URL, ऐसा ऑप्शन जो यह method नहीं लेता, जैसे हर कॉल पर दिया गया pollingInterval, version के रूप में avatar, या ऐसा proxy type जिसे CapSkip स्वीकार नहीं करताजाँचें कि key मिली थी, और संदेश में बताए गए ऑप्शन को ठीक करें या हटा दें
पहली कॉल पर ERROR_KEY_DOES_NOT_EXIST या ERROR_WRONG_USER_KEY वाला ApiExceptionCapSkip में API key validation चालू है, और script ने डिफ़ॉल्ट key भेजी या कोई key नहीं भेजीCAPSKIP_API_KEY को CapSkip में कॉन्फ़िगर की गई किसी key पर सेट करें
हर कॉल में लगभग चार सेकंड लगते हैंpoll के बीच का अंतर डिफ़ॉल्ट 5 सेकंड की ऊपरी सीमा की ओर दोगुना होता जाता है, इसलिए दो सेकंड पर तैयार जवाब 3.75 सेकंड वाले poll का इंतज़ार करता हैconstructor में pollingInterval: 0.5 सेट करें
भारी parallel लोड में TimeoutExceptionएक साथ चल रहे लॉगिन उससे ज़्यादा हैं जितने 10 threads 120 सेकंड के भीतर निपटा सकते हैं, या solve के बीच में CapSkip रुक गयाpool को Max. Threads तक सीमित रखें, या उस कॉल के लिए लंबा timeout पास करें
पहली कॉल पर NetworkException, जिसके संदेश में ECONNREFUSED या ETIMEDOUT होता है, या host कोई नाम हो तो err.cause.code मेंCapSkip चल नहीं रहा, host या port गलत है, या कोई फ़ायरवॉल कनेक्शन ड्रॉप कर देता हैCapSkip शुरू करें, फिर देखें कि उसे Local मोड में होना चाहिए या Server मोड में

FAQ

क्या यहाँ capskip पैकेज TypeScript के साथ काम करता है?

हाँ, इसके साथ इसकी अपनी type definitions आती हैं। Capy के लिए दो बातें मायने रखती हैं। result type, captchakey, challengekey और answer को वैकल्पिक बताता है, क्योंकि दूसरे methods इन्हें सेट नहीं करते। इसलिए strict mode में इन्हें body.set को पास करने से पहले एक जाँच या non-null assertion चाहिए। और options type कोई भी key स्वीकार करता है, इसलिए गलत लिखा गया या गलत जगह रखा गया ऑप्शन, जैसे हर कॉल पर पास किया गया pollingInterval, बिना किसी शिकायत के compile हो जाता है और run time पर ValidationException के साथ फेल होता है।

क्या node user agent की वजह से जवाब ठुकरा दिया जाता है?

Capy की तरफ़ से नहीं। Capy का जवाब किसी ब्राउज़र से बँधा नहीं होता, CapSkip इसके लिए कोई user agent नहीं लौटाता, और जो वैकल्पिक user agent आप पास कर सकते हैं, वह सिर्फ़ पज़ल लाने वाली रिक्वेस्ट को बदलता है। फिर भी साइट खुद, अपनी वजहों से, ऐसी रिक्वेस्ट ठुकरा सकती है जिसका user agent node हो। अगर ऐसा हो, तो पेज fetch और पोस्ट, दोनों में किसी ब्राउज़र का user-agent header जोड़ें।

यह Python वर्ज़न से कैसे अलग है?

कॉल, ठहराव और नियम वही हैं। Python में requests.Session आपके लिए cookies रखता है, जबकि Node में आप उन्हें खुद आगे ले जाते हैं, और नतीजा वहाँ एक dict होता है और यहाँ एक object। Python वाला पहलू यहाँ कवर किया गया है: Python में Capy Puzzle हल करने की गाइड.

क्या किसी होस्टेड प्लेटफ़ॉर्म पर चल रही Node script सॉल्वर तक पहुँच सकती है?

हाँ। कनेक्शन सेटिंग्स में CapSkip को Server मोड पर कर दें ताकि वह loopback के बजाय किसी नेटवर्क पते पर सुने, वह पता अपनी script में CAPSKIP_HOST से पढ़ें, और उसे constructor को पास करें। VPS, कंटेनर होस्ट, CI runner और serverless function, सब उसी HTTP API के ज़रिए जुड़ते हैं। जब रास्ता इंटरनेट से होकर जाए, तो स्टैटिक पब्लिक IP इस्तेमाल करें, साथ में उन पतों के लिए Windows Firewall में एक block नियम रखें जिन्हें पोर्ट तक नहीं पहुँचना चाहिए। सॉल्वर आपके अपने हार्डवेयर पर ही रहता है, इसलिए solve गिनने के तरीके में कुछ नहीं बदलता।

संक्षेप में

Node.js में Capy Puzzle हल करने के लिए, पेज को एक cookie jar में fetch करें, एक regex से PUZZLE_ key निकालें, और widget script का host नोट कर लें, कहीं वह डिफ़ॉल्ट न हो। pollingInterval: 0.5 के साथ बने क्लाइंट से solver.capy() को key और असली पेज URL के साथ कॉल करें, और उसे अपने लगभग दो सेकंड लेने दें। captchakey, challengekey और answer को तीनों capy_ फ़ील्ड में पोस्ट करें, हूबहू, एक साथ और तुरंत, पेज की cookies के साथ और पेज को Referer बनाकर, और 302 जो cookie वापस देता है, उसे रख लें। कभी-कभार की चूक पर दोबारा कोशिश करें, एक साथ चल रहे लॉगिन Max. Threads से ज़्यादा न रखें, और जैसे ही script सॉल्वर की मशीन से बाहर जाए, Server मोड पर चले जाएँ।

उस दोबारा कोशिश के बारे में एक आख़िरी बात। हर कोशिश में एक अलग पज़ल आता है, इसलिए चूक का सही इलाज दोबारा कोशिश करना ही है, और अपनी ही मशीन पर चलने वाले लोकल captcha सॉल्वर के साथ दूसरी कोशिश की कीमत बस कुछ सेकंड होती है, एक और बिल किया गया solve नहीं।