use crate::Page;
use anyhow::Result;
use async_trait::async_trait;
use super::{parse_detection_result, CaptchaInfo, Detector};
pub struct CanvasCaptchaDetector;
impl CanvasCaptchaDetector {
pub const JS: &'static str = r#"(function() {
const canvas = document.querySelector(
'canvas.captcha, #captcha canvas, ' +
'[class*="canvas-captcha"], [class*="svg-captcha"], ' +
'[class*="math-captcha"], [class*="icon-captcha"], ' +
'[class*="color-captcha"], [class*="rotated-text"], ' +
// Generic "image-as-challenge" wrappers: a captcha-image
// class containing a visual + an answer input.
'.captcha-img, .captcha-image, ' +
// Pickers (icon / color) and homegrown captcha boxes by ID.
'.icon-grid, .color-grid, ' +
'[id*="captcha-box"], [id*="captcha-input"], ' +
// Game-style captchas (kill enemies, click targets, etc.).
'[class*="game-captcha"], [class*="doom-captcha"]'
);
if (canvas) {
return { kind: 'canvas', site_key: null, container: 'canvas.captcha' };
}
return { kind: 'none', site_key: null, container: null };
})()"#;
}
#[async_trait]
impl Detector for CanvasCaptchaDetector {
fn name(&self) -> &'static str {
"canvas_captcha"
}
fn priority(&self) -> i32 {
65
}
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 canvas_captcha_detector_js_has_selectors() {
assert!(CanvasCaptchaDetector::JS.contains("canvas.captcha"));
assert!(CanvasCaptchaDetector::JS.contains("#captcha canvas"));
assert!(CanvasCaptchaDetector::JS.contains("[class*=\"canvas-captcha\"]"));
assert!(CanvasCaptchaDetector::JS.contains("[class*=\"svg-captcha\"]"));
assert!(CanvasCaptchaDetector::JS.contains("[class*=\"math-captcha\"]"));
assert!(CanvasCaptchaDetector::JS.contains("[class*=\"icon-captcha\"]"));
assert!(CanvasCaptchaDetector::JS.contains("[class*=\"color-captcha\"]"));
assert!(CanvasCaptchaDetector::JS.contains("[class*=\"rotated-text\"]"));
}
#[test]
fn canvas_captcha_detector_js_has_extended_selectors() {
assert!(CanvasCaptchaDetector::JS.contains(".captcha-img"));
assert!(CanvasCaptchaDetector::JS.contains(".captcha-image"));
assert!(CanvasCaptchaDetector::JS.contains(".icon-grid"));
assert!(CanvasCaptchaDetector::JS.contains(".color-grid"));
assert!(CanvasCaptchaDetector::JS.contains("[id*=\"captcha-box\"]"));
assert!(CanvasCaptchaDetector::JS.contains("[id*=\"captcha-input\"]"));
}
}