fiptcha 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fiptcha",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "Local CAPTCHA solving for browser automation. Built for Figranium, usable anywhere.",
5
5
  "main": "index.js",
6
6
  "license": "Apache-2.0",
@@ -1,5 +1,4 @@
1
1
  const { captchaModelManager, checksumBuffer } = require('./captcha-model-manager');
2
- const { clickVisibleTarget } = require('./captcha-pointer');
3
2
 
4
3
  const PROVIDERS = Object.freeze({
5
4
  recaptcha_v2: Object.freeze({
@@ -127,6 +126,16 @@ async function classifyGrid(frame, adapter, cells, label, seenTiles) {
127
126
  return selected;
128
127
  }
129
128
 
129
+ async function rememberClickedTile(cells, index, seenTiles) {
130
+ // Clicking a CAPTCHA tile changes its rendered appearance (selection overlay,
131
+ // border, checkmark, etc.). Remember that post-click state immediately so the
132
+ // next sweep does not mistake the selection UI itself for a replacement image
133
+ // and toggle an already-correct tile back off. A genuinely replaced tile will
134
+ // still produce a different fingerprint on the next sweep.
135
+ const image = await cells.nth(index).screenshot({ type: 'png' }).catch(() => null);
136
+ if (image) seenTiles.set(index, checksumBuffer(image));
137
+ }
138
+
130
139
  async function visibleText(frame, selector) {
131
140
  const matches = frame.locator(selector);
132
141
  for (let index = 0; index < await matches.count().catch(() => 0); index += 1) {
@@ -161,17 +170,19 @@ async function solveImageGrid(page, { captchaType, deadline, waitForToken, logs
161
170
  const count = await cells.count();
162
171
  if (![9, 16].includes(count)) throw new Error(`Unsupported ${captchaType} grid size (${count} cells)`);
163
172
  const selected = await classifyGrid(frame, adapter, cells, prompt, seenTiles);
173
+ // Grid cells live inside a provider iframe. Let Playwright target the
174
+ // element in that frame instead of sending a page-level mouse click:
175
+ // the latter can miss the cell when iframe coordinates change.
164
176
  for (const index of selected) {
165
- const clicked = await clickVisibleTarget(page, cells.nth(index));
166
- if (!clicked) throw new Error(`${captchaType} grid tile was outside the viewport`);
177
+ await cells.nth(index).click({ timeout: 2000 });
178
+ await rememberClickedTile(cells, index, seenTiles);
167
179
  }
168
180
  totalSelections += selected.length;
169
181
  if (!selected.length) break;
170
182
  await page.waitForTimeout(600);
171
183
  }
172
184
  logs.push(`Local ${captchaType} grid round ${round + 1}: selected ${totalSelections} cells for "${prompt}"`);
173
- const submitted = await clickVisibleTarget(page, frame.locator(totalSelections ? adapter.submit : adapter.noMatch).first());
174
- if (!submitted) throw new Error(`${captchaType} verification button was outside the viewport`);
185
+ await frame.locator(totalSelections ? adapter.submit : adapter.noMatch).first().click({ timeout: 2000 });
175
186
  const token = await waitForToken(page, captchaType, Math.min(2500, Math.max(0, deadline - Date.now())));
176
187
  if (token) return token;
177
188
  await page.waitForTimeout(350);
@@ -179,4 +190,4 @@ async function solveImageGrid(page, { captchaType, deadline, waitForToken, logs
179
190
  return null;
180
191
  }
181
192
 
182
- module.exports = { PROVIDERS, normalizePrompt, normalizeModelLabel, normalizeBox, pngDimensions, mapDetectionsToCells, classifyGrid, visibleText, solveImageGrid };
193
+ module.exports = { PROVIDERS, normalizePrompt, normalizeModelLabel, normalizeBox, pngDimensions, mapDetectionsToCells, classifyGrid, rememberClickedTile, visibleText, solveImageGrid };