Skip to content

Commit

Permalink
Merge pull request #1 from arghyadipchak/master
Browse files Browse the repository at this point in the history
Code Refactor
  • Loading branch information
vlourme committed Nov 16, 2023
2 parents cdf50e1 + ef984a1 commit a20f750
Show file tree
Hide file tree
Showing 7 changed files with 216 additions and 277 deletions.
3 changes: 3 additions & 0 deletions .rustfmt.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
edition = "2021"
max_width = 80
tab_spaces = 4
10 changes: 5 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ license = "MIT"
keywords = ["captcha", "amazon", "amazon-captcha", "amazon-captcha-rs"]
categories = ["multimedia::images", "web-programming"]

[dev-dependencies]
reqwest = "0.11.20"
tokio = { version = "1", features = ["full"] }
regex = "1"

[dependencies]
image = "0.24.7"
bincode = "1.3.3"

[dev-dependencies]
reqwest = "0.11.20"
tokio = { version = "1.32.0", features = ["macros", "rt-multi-thread"] }
regex = "1.9.6"
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ Amazon captchas exhibit a repetitive nature, with characters consistently less t

## Usage Example
```rust
use amazon_captcha_rs::new_solver;
use amazon_captcha_rs::Solver;

let image = image::open("captcha.jpg").unwrap();

let solver = new_solver();
let response = solver.resolve_image(&image).unwrap();
let solver = Solver::new().unwrap();
let response = solver.resolve_image(&image);

assert_eq!(response, "caggpa");
```
Expand Down
55 changes: 19 additions & 36 deletions examples/benchmark.rs
Original file line number Diff line number Diff line change
@@ -1,63 +1,46 @@
//! Test the precision of the captcha solver

use std::time::Instant;
use std::{fs, time::Instant};

extern crate amazon_captcha_rs;
use amazon_captcha_rs::Solver;

fn main() {
let start = Instant::now();
let files = std::fs::read_dir("examples/dataset").unwrap();
let solver = amazon_captcha_rs::new_solver();
let solver = Solver::new().unwrap();

let mut solved = 0;
let mut total = 0;
let (mut resolved, mut total) = (0u32, 0u32);
let mut times = Vec::new();

files.for_each(|file| {
let now = Instant::now();
let file = file.unwrap();
let path = file.path();

for file in fs::read_dir("examples/dataset").unwrap() {
let path = file.unwrap().path();
let expect = path
.as_path()
.file_name()
.unwrap()
.unwrap_or_default()
.to_str()
.unwrap()
.split(".")
.unwrap_or_default()
.split('.')
.next()
.unwrap();
.unwrap_or_default();

if expect.len() < 6 {
return;
}

total += 1;
let img = image::open(&path).unwrap();

let Some(result) = solver.resolve_image(&img) else {
println!("{:?}: Failed to resolve", &path.as_path());
return;
};
let now = Instant::now();
let result = solver.resolve_image(&image::open(&path).unwrap());
times.push(now.elapsed().as_millis());

if expect == result {
solved += 1;
resolved += 1;
} else {
println!(
"{:?}: Expect '{}', got '{}'",
&path.as_path(),
expect,
result
);
eprintln!("{path:?}: Expected '{expect}', got '{result}'");
}
}

times.push(now.elapsed().as_millis());
});

println!("Solved: {}/{}", solved, total);
println!("Precision: {:.2}%", solved as f32 / total as f32 * 100.0);
println!(
"Average time: {:.2}ms",
"Resolved: {resolved}/{total}\nPrecision: {:.2}%\nAverage Time: {:.2}ms",
resolved as f32 / total as f32 * 100.0,
times.iter().sum::<u128>() as f32 / times.len() as f32
);
println!("Total time: {:.2}s", start.elapsed().as_secs_f32());
}
84 changes: 46 additions & 38 deletions examples/benchmark_infinite.rs
Original file line number Diff line number Diff line change
@@ -1,56 +1,64 @@
//! Benchmark running on an infinite loop.

use std::{error, time::Instant};

use amazon_captcha_rs::Solver;
use image::EncodableLayout;
use regex::Regex;
use reqwest::Client;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let solver = amazon_captcha_rs::new_solver();
async fn main() -> Result<(), Box<dyn error::Error>> {
let solver = Solver::new()?;

let mut resolved = 0;
let mut total = 0;
let code_regex = Regex::new(r#"name="amzn" value="(.*?)" \/>"#)?;
let img_regex = Regex::new(r#"<img src="((.*).jpg)">"#)?;

loop {
let ok = resolve_captcha(&solver).await?;
let (mut resolved, mut total) = (0u32, 0u32);
let mut times = Vec::new();

loop {
total += 1;
if ok {
resolved += 1;
}

if total % 10 == 0 {
println!("Resolved: {}/{}", resolved, total);
println!("Precision: {:.2}%", resolved as f32 / total as f32 * 100.0);
}
}
}

async fn resolve_captcha(solver: &Solver) -> Result<bool, Box<dyn std::error::Error>> {
let text = reqwest::get("https://www.amazon.com/errors/validateCaptcha")
.await?
.text()
.await?;
let text =
reqwest::get("https://www.amazon.com/errors/validateCaptcha")
.await?
.text()
.await?;

let img_regex = Regex::new(r#"<img src="((.*).jpg)">"#)?;
let cap = img_regex.captures(&text).unwrap();
let url = cap.get(1).unwrap().as_str();
let code = code_regex.captures(&text).unwrap().get(1).unwrap().as_str();

let code_regex = Regex::new(r#"name="amzn" value="(.*?)" \/>"#).unwrap();
let code = code_regex.captures(&text).unwrap().get(1).unwrap().as_str();
let url = img_regex.captures(&text).unwrap().get(1).unwrap().as_str();
let img = image::load_from_memory(
reqwest::get(url).await?.bytes().await?.as_bytes(),
)?;

let img = reqwest::get(url).await?.bytes().await?;
let img = image::load_from_memory(img.as_bytes())?;
let now = Instant::now();
let result = solver.resolve_image(&img);
times.push(now.elapsed().as_millis());

let Some(result) = solver.resolve_image(&img) else {
return Ok(false);
};

let response = reqwest::Client::new()
.get("https://www.amazon.com/errors/validateCaptcha")
.query(&[("amzn", code), ("amzn-r", "/"), ("field-keywords", &result)])
.send()
.await?;
if Client::new()
.get("https://www.amazon.com/errors/validateCaptcha")
.query(&[
("amzn", code),
("amzn-r", "/"),
("field-keywords", &result),
])
.send()
.await?
.url()
.to_string()
== "https://www.amazon.com/"
{
resolved += 1;
}

Ok(response.url().to_string() == "https://www.amazon.com/")
if total % 10 == 0 {
println!(
"Resolved: {resolved}/{total}\nPrecision: {:.2}%\nAverage Time: {:.2}ms",
resolved as f32 / total as f32 * 100.0,
times.iter().sum::<u128>() as f32 / times.len() as f32
);
}
}
}
39 changes: 17 additions & 22 deletions examples/dataset.rs
Original file line number Diff line number Diff line change
@@ -1,36 +1,31 @@
//! Download captcha image to test precision

use std::{error, fs};

use regex::Regex;
use std::{fs::File, io::Write};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
async fn main() -> Result<(), Box<dyn error::Error>> {
println!("Downloading captcha images...");

for i in 0..100 {
let img = download_captcha().await?;

let path = format!("examples/dataset/{}.jpg", i);
let img_regex = Regex::new(r#"<img src="((.*).jpg)">"#)?;

File::create(path)?.write_all(&img)?;
for i in 0..100 {
let text =
reqwest::get("https://www.amazon.com/errors/validateCaptcha")
.await?
.text()
.await?;

let url = img_regex.captures(&text).unwrap().get(1).unwrap().as_str();

fs::write(
format!("examples/dataset/{i}.jpg"),
reqwest::get(url).await?.bytes().await?,
)?;
}

println!("Done!");

Ok(())
}

async fn download_captcha() -> Result<Vec<u8>, Box<dyn std::error::Error>> {
let text = reqwest::get("https://www.amazon.com/errors/validateCaptcha")
.await?
.text()
.await?;

let re = Regex::new(r#"<img src="((.*).jpg)">"#)?;
let cap = re.captures(&text).unwrap();
let url = cap.get(1).unwrap().as_str();

let img = reqwest::get(url).await?.bytes().await?;

Ok(img.to_vec())
}
Loading

0 comments on commit a20f750

Please sign in to comment.