Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Code Refactor #1

Merged
merged 3 commits into from
Nov 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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