use crate::Page;
use anyhow::Result;
use async_trait::async_trait;
use super::{parse_detection_result, CaptchaInfo, Detector};
pub struct SliderCaptchaDetector;
impl SliderCaptchaDetector {
pub const JS: &'static str = r#"(function() {
/* Specific class names + targeted attribute-contains selectors.
A bare contains-selector for the bare word `rotate` would also
match `rotated-text` (a static-text captcha, wrong solver),
so we require the `-captcha` suffix throughout. ROOT classes:
`slider-captcha`, `swipe-captcha`, `rotate-captcha`,
`motion-captcha` plus the bare ID `#slider`. */
const slider = document.querySelector(
'.slider-captcha, #slider, ' +
'.swipe-captcha, .rotate-captcha, .motion-captcha, ' +
'[class*="slider-captcha"], [class*="swipe-captcha"], ' +
'[class*="rotate-captcha"], [class*="motion-captcha"]'
);
if (slider) {
return { kind: 'slider', site_key: null, container: '.slider-captcha' };
}
const rangeInCaptcha = document.querySelector('.captcha input[type="range"], #captcha input[type="range"], [class*="captcha"] input[type="range"]');
if (rangeInCaptcha) {
return { kind: 'slider', site_key: null, container: '.captcha input[type="range"]' };
}
const canvasDrag = document.querySelector('canvas[class*="drag"], canvas[class*="slider"], canvas[class*="swipe"]');
if (canvasDrag) {
return { kind: 'slider', site_key: null, container: 'canvas[class*="drag"]' };
}
return { kind: 'none', site_key: null, container: null };
})()"#;
}
#[async_trait]
impl Detector for SliderCaptchaDetector {
fn name(&self) -> &'static str {
"slider_captcha"
}
fn priority(&self) -> i32 {
60
}
async fn detect(&self, page: &Page) -> Result<Option<CaptchaInfo>> {
let raw = page.evaluate(Self::JS).await?;
let val = raw.into_value::<serde_json::Value>()?;
Ok(parse_detection_result(val))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn slider_captcha_detector_js_has_selectors() {
let js = SliderCaptchaDetector::JS;
assert!(js.contains(".slider-captcha"));
assert!(js.contains("#slider"));
assert!(js.contains(".swipe-captcha"));
assert!(js.contains(".rotate-captcha"));
assert!(js.contains(".motion-captcha"));
assert!(js.contains("input[type=\"range\"]"));
assert!(js.contains("canvas[class*=\"drag\"]"));
}
#[test]
fn slider_captcha_detector_does_not_overmatch_rotated_text() {
let js = SliderCaptchaDetector::JS;
assert!(
!js.contains(r#"[class*="rotate"]"#),
"must not match bare `rotate` substring (use `rotate-captcha` instead)"
);
assert!(
!js.contains(r#"[class*="motion"]"#),
"must not match bare `motion` substring (use `motion-captcha` instead)"
);
assert!(js.contains("[class*=\"rotate-captcha\"]"));
assert!(js.contains("[class*=\"motion-captcha\"]"));
}
}