use anyhow::Result;
use async_trait::async_trait;
use chromiumoxide::Page;
use super::{parse_detection_result, CaptchaInfo, Detector};
pub struct PowCaptchaDetector;
impl PowCaptchaDetector {
pub const JS: &'static str = r#"(function() {
const selectors = [
'.altcha', '[data-altcha]',
'.mcaptcha', '[data-mcaptcha]',
'.friendly-captcha', '[data-friendly-captcha]',
'.cap-dev', '[data-cap-dev]'
];
for (const sel of selectors) {
if (document.querySelector(sel)) {
return { kind: 'pow', site_key: null, container: sel };
}
}
const scripts = document.querySelectorAll('script[src]');
for (const s of scripts) {
const src = s.getAttribute('src') || '';
if (src.includes('altcha') || src.includes('mcaptcha') || src.includes('friendlycaptcha') || src.includes('cap.dev')) {
const match = src.includes('altcha') ? 'altcha'
: src.includes('mcaptcha') ? 'mcaptcha'
: src.includes('friendlycaptcha') ? 'friendlycaptcha'
: 'cap.dev';
return { kind: 'pow', site_key: null, container: 'script[src*="' + match + '"]' };
}
}
return { kind: 'none', site_key: null, container: null };
})()"#;
}
#[async_trait]
impl Detector for PowCaptchaDetector {
fn name(&self) -> &'static str {
"pow_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 pow_captcha_detector_js_has_selectors() {
assert!(PowCaptchaDetector::JS.contains(".altcha"));
assert!(PowCaptchaDetector::JS.contains("[data-altcha]"));
assert!(PowCaptchaDetector::JS.contains(".mcaptcha"));
assert!(PowCaptchaDetector::JS.contains("[data-mcaptcha]"));
assert!(PowCaptchaDetector::JS.contains(".friendly-captcha"));
assert!(PowCaptchaDetector::JS.contains("[data-friendly-captcha]"));
assert!(PowCaptchaDetector::JS.contains(".cap-dev"));
assert!(PowCaptchaDetector::JS.contains("[data-cap-dev]"));
}
}