| Line 243... |
Line 243... |
| 243 |
/** realme rebuilt the page; the old .sn-input / div.check-btn no longer render. */
|
243 |
/** realme rebuilt the page; the old .sn-input / div.check-btn no longer render. */
|
| 244 |
private static final String IMEI_INPUT = ".imei-input input";
|
244 |
private static final String IMEI_INPUT = ".imei-input input";
|
| 245 |
private static final String SUBMIT_ICON = ".imei-input .search-icon";
|
245 |
private static final String SUBMIT_ICON = ".imei-input .search-icon";
|
| 246 |
|
246 |
|
| 247 |
/**
|
247 |
/**
|
| 248 |
* Slider px to nudge while working out how far the puzzle piece travels per slider px.
|
248 |
* Calibration nudge, in slider px, sized from the gap rather than fixed.
|
| - |
|
249 |
*
|
| 249 |
* Was 15, which is why the captcha solved so rarely: the piece moves ~1.7px per slider
|
250 |
* A fixed 15px was far too small: it closed only ~25px, so the ratio came from a
|
| 250 |
* px, so 15px closed the gap by only ~25px, and Hough detection is worth +/-2-3px per
|
251 |
* measurement ~20% noise and the aim landed ~30px out. A fixed 60px then proved too
|
| 251 |
* circle -- a ratio derived from a measurement ~20% wrong, then multiplied across the
|
252 |
* large in the other direction -- production data showed 85% of attempts with a gap
|
| 252 |
* whole remaining travel. 60px closes ~100px, so the same noise is ~5%.
|
253 |
* under 60px ended up UNUSABLE (the piece shot past the hole and the measured gap
|
| - |
|
254 |
* grew), and 45% overall.
|
| - |
|
255 |
*
|
| - |
|
256 |
* Aim to close about half the gap: the piece travels roughly ASSUMED_RATIO px per
|
| - |
|
257 |
* slider px, so nudge ~= gap / (2 * ratio), clamped. The real ratio is still derived
|
| - |
|
258 |
* from the measurement; this only sizes the probe.
|
| 253 |
*/
|
259 |
*/
|
| - |
|
260 |
private static final double ASSUMED_RATIO = 1.7;
|
| - |
|
261 |
private static final int MIN_NUDGE = 10;
|
| 254 |
private static final int CALIBRATION_NUDGE = 60;
|
262 |
private static final int MAX_NUDGE = 60;
|
| 255 |
private static final double ALIGNED_PX = 2.0;
|
263 |
private static final double ALIGNED_PX = 2.0;
|
| 256 |
|
264 |
|
| - |
|
265 |
private static int nudgeFor(double gap) {
|
| - |
|
266 |
int n = (int) Math.round(gap / (2 * ASSUMED_RATIO));
|
| - |
|
267 |
return Math.max(MIN_NUDGE, Math.min(MAX_NUDGE, n));
|
| - |
|
268 |
}
|
| - |
|
269 |
|
| 257 |
/** Stash the active/check response on window as the page receives it. */
|
270 |
/** Stash the active/check response on window as the page receives it. */
|
| 258 |
private void installResponseCapture(WebDriver driver) {
|
271 |
private void installResponseCapture(WebDriver driver) {
|
| 259 |
try {
|
272 |
try {
|
| 260 |
((JavascriptExecutor) driver).executeScript(
|
273 |
((JavascriptExecutor) driver).executeScript(
|
| 261 |
"if (!window.__rmCapInstalled) {"
|
274 |
"if (!window.__rmCapInstalled) {"
|
| Line 316... |
Line 329... |
| 316 |
}
|
329 |
}
|
| 317 |
}
|
330 |
}
|
| 318 |
|
331 |
|
| 319 |
/** @return {sliderPxToMove, sliderPxPerCirclePx}, or null if the circles were unreadable. */
|
332 |
/** @return {sliderPxToMove, sliderPxPerCirclePx}, or null if the circles were unreadable. */
|
| 320 |
private double[] calibrate(WebDriver driver, Actions actionProvider, double distance) throws Exception {
|
333 |
private double[] calibrate(WebDriver driver, Actions actionProvider, double distance) throws Exception {
|
| - |
|
334 |
int nudge = nudgeFor(distance);
|
| 321 |
actionProvider.moveByOffset(CALIBRATION_NUDGE, 0).perform();
|
335 |
actionProvider.moveByOffset(nudge, 0).perform();
|
| 322 |
|
336 |
|
| 323 |
String fileName2 = "/tmp/" + Thread.currentThread().getName() + "-moved.png";
|
337 |
String fileName2 = "/tmp/" + Thread.currentThread().getName() + "-moved.png";
|
| 324 |
double[] movedCircles = getMatCircles2(driver, fileName2);
|
338 |
double[] movedCircles = getMatCircles2(driver, fileName2);
|
| 325 |
if (movedCircles == null) return null;
|
339 |
if (movedCircles == null) return null;
|
| 326 |
|
340 |
|
| 327 |
double movedDistance = Math.abs(movedCircles[0] - movedCircles[1]);
|
341 |
double movedDistance = Math.abs(movedCircles[0] - movedCircles[1]);
|
| 328 |
double closed = distance - movedDistance;
|
342 |
double closed = distance - movedDistance;
|
| 329 |
LOGGER.info("Calibration: gap {} -> {} after {}px, closed {}",
|
343 |
LOGGER.info("Calibration: gap {} -> {} after {}px, closed {}",
|
| 330 |
distance, movedDistance, CALIBRATION_NUDGE, closed);
|
344 |
distance, movedDistance, nudge, closed);
|
| 331 |
if (closed <= 0) return null;
|
345 |
if (closed <= 0) return null;
|
| 332 |
|
346 |
|
| 333 |
double sliderPerCircle = CALIBRATION_NUDGE / closed;
|
347 |
double sliderPerCircle = nudge / closed;
|
| 334 |
return new double[]{Math.round(movedDistance * sliderPerCircle), sliderPerCircle};
|
348 |
return new double[]{Math.round(movedDistance * sliderPerCircle), sliderPerCircle};
|
| 335 |
}
|
349 |
}
|
| 336 |
|
350 |
|
| 337 |
/** Measure again after the main move and close the remainder before releasing. */
|
351 |
/** Measure again after the main move and close the remainder before releasing. */
|
| 338 |
private int refine(WebDriver driver, Actions actionProvider, double sliderPerCircle) throws Exception {
|
352 |
private int refine(WebDriver driver, Actions actionProvider, double sliderPerCircle) throws Exception {
|