Resolva reCAPTCHA Enterprise com Rust
Use a API REST do solvethree em Rust: crie uma tarefa, consulte seu status até que esteja pronta e envie solution.token ao destino usando a identidade de navegador retornada e o mesmo proxy. O exemplo abaixo usa reqwest, Tokio e serde_json diretamente — não um SDK oficial para Rust.
Nesta página
Configure o site e a solicitação
Primeiro, crie um perfil ativo para o hostname de destino no console do seu espaço de trabalho. Enviar um hostname e uma ação não cria um perfil. Consulte a configuração de perfil de site se o destino ainda não estiver configurado.
Defina estas variáveis de ambiente no servidor em que o cliente Rust será executado:
SOLVETHREE_API_KEY: a chave de API do seu espaço de trabalho. Mantenha-a no servidor.SOLVETHREE_PROXY: a URL do proxy para a resolução e a solicitação subsequente ao destino.REQUEST_ID: um ID opcional e estável para esta tarefa lógica. Salve-o antes de enviar a solicitação para permitir a recuperação após uma interrupção da conexão.
| Campo | O que enviar |
|---|---|
site | O hostname de destino. Substitua accounts.example.com no exemplo. |
action | A ação esperada pelo destino. Substitua login, se necessário. |
proxy | A URL de proxy única lida de SOLVETHREE_PROXY. |
requestId | Uma chave de idempotência contendo de 1–64 letras ASCII, números, sublinhados ou hífens. |
recaptchaType | Omitido aqui porque o padrão é enterprise-v3. |
Não adicione os campos type ou url a esta solicitação reCAPTCHA. É necessário fornecer exatamente um dos campos proxy ou proxyCandidates; o exemplo usa proxy para que o envio ao destino possa usar a mesma conexão.
Crie o cliente Rust
Coloque o arquivo de dependências em Cargo.toml e o cliente em src/main.rs de um projeto binário Cargo. Após definir as variáveis de ambiente e alterar o hostname de destino e a ação, execute o projeto com Cargo. As dependências fixam reqwest na versão 0.13.5.
[package]
name = "solvethree-example"
version = "0.1.0"
edition = "2021"
[dependencies]
reqwest = { version = "=0.13.5", features = ["json"] }
serde_json = "1"
tokio = { version = "1", features = ["macros", "rt-multi-thread", "time"] }use std::{env, error::Error, time::{Duration, SystemTime, UNIX_EPOCH}};
use reqwest::{Client, redirect::Policy};
use serde_json::{json, Value};
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let api = "https://api.solvethree.com/v1";
let key = env::var("SOLVETHREE_API_KEY")?;
let request_id = env::var("REQUEST_ID").unwrap_or_else(|_| format!("rust-{}", SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_nanos()));
let input = json!({"site":"accounts.example.com", "action":"login", "proxy":env::var("SOLVETHREE_PROXY")?, "requestId":request_id});
let client = Client::builder().redirect(Policy::none()).timeout(Duration::from_secs(30)).build()?;
let mut task_id: Option<String> = None;
let outcome = tokio::time::timeout(Duration::from_secs(180), async {
let response = client.post(format!("{api}/tasks?waitMs=0")).bearer_auth(&key).json(&input).send().await?;
if !response.status().is_success() { return Err(format!("API HTTP {}", response.status()).into()); }
let mut result: Value = response.json().await?;
let id = result["id"].as_str().ok_or("Missing task ID")?.to_owned();
if !id.bytes().all(|b| b.is_ascii_alphanumeric() || b == b'_' || b == b'-') { return Err("Invalid task ID".into()); }
task_id = Some(id.clone());
loop {
match result["status"].as_str() {
Some("ready") => return Ok::<Value, Box<dyn Error>>(result),
Some("failed") => return Err(format!("Task failed: {}", result["errorCode"]).into()),
Some("pending") | Some("solving") => {},
_ => return Err("Unexpected task status".into()),
}
tokio::time::sleep(Duration::from_secs(1)).await;
let response = client.get(format!("{api}/tasks/{id}?waitMs=15000")).bearer_auth(&key).send().await?;
if !response.status().is_success() { return Err(format!("API HTTP {}", response.status()).into()); }
result = response.json().await?;
}
}).await;
match outcome {
Ok(Ok(result)) => {
println!("Ready task: {}", result["id"].as_str().unwrap_or("unknown"));
// Submit result["solution"]["token"] with returned browser headers
// through the original proxy; do not log the solution.
Ok(())
},
Ok(Err(error)) => {
eprintln!("Recovery requestId={request_id} taskId={task_id:?}");
Err(error)
},
Err(_) => {
eprintln!("Recovery requestId={request_id} taskId={task_id:?}");
Err("Local deadline reached; recover the existing task".into())
}
}
}O cliente faz uma única solicitação de criação para POST /v1/tasks, autenticando-se com um token Bearer. Ele guarda o ID da tarefa retornado e usa GET /v1/tasks/:id nas solicitações de status subsequentes.
Dois limites impedem que o cliente espere indefinidamente: o tempo limite do reqwest limita cada solicitação HTTP a 30 segundos, enquanto o tempo limite do Tokio limita a criação e as consultas, juntas, a 180 segundos. Essas são escolhas do lado do cliente, não garantias de conclusão do serviço. Os redirecionamentos são desabilitados pelo ClientBuilder do reqwest.
Apenas pending e solving mantêm o loop de consulta. ready retorna o resultado, failed interrompe com um erro, e um status inesperado também interrompe, em vez de consultar indefinidamente. O fluxo de sucesso exibe o ID da tarefa, não o token.
Recupere uma solicitação interrompida sem outra resolução
O exemplo mantém requestId e o ID da tarefa retornado na memória e os exibe quando ocorre um erro HTTP, de transporte, de análise ou de prazo excedido. Ele não os persiste após o encerramento do processo. Para recuperação após reinicialização, armazene o ID da solicitação e os dados de entrada originais da tarefa antes da criação e armazene o ID da tarefa assim que ele chegar.
- 1.
Se você tem um ID de tarefa, consulte essa tarefa
Retome com GET /v1/tasks/:id em vez de criar outra tarefa. O ponto de entrada normal do exemplo sempre cria uma tarefa; um fluxo de reinicialização deve usar o ID salvo.
- 2.
Se você tiver apenas um ID de solicitação, recupere a criação
Envie a mesma solicitação de tarefa com o mesmo requestId. Isso retorna a tarefa existente. Alterar os dados de entrada reutilizando o ID causa um conflito.
- 3.
Trate o status recuperado
Continue consultando tarefas com status pending ou solving. Use uma solução com status ready ou pare no status failed e inspecione errorCode e error. Um tempo limite local excedido, por si só, não comprova que a tarefa remota falhou.
Falhas HTTP e falhas de tarefa exigem tratamentos diferentes. O cliente verifica respostas HTTP sem sucesso antes de interpretar os dados de sucesso. Já uma falha terminal de tarefa chega com o status failed; resoluções com falha são reembolsadas. A mensagem de falha do exemplo inclui apenas errorCode, portanto, inspecione também error na resposta da tarefa ao diagnosticá-la. Consulte tratamento de erros da API antes de tentar novamente com os mesmos dados de entrada.
Envie o token com sua identidade de navegador
O exemplo para quando a tarefa fica pronta; ele não envia a solicitação da aplicação ao destino. Essa solicitação precisa de solution.token, não do ID da tarefa. Use a identidade retornada em userAgent e secChUa pelo proxy original e envie ao destino pretendido, sem misturar valores de outra sessão de navegador.
Uma tarefa pronta significa que uma solução foi gerada, não que o destino aceitou a solicitação da sua aplicação. Adicione o envio específico do destino no comentário marcado no fluxo de sucesso; consulte o guia de integração do reCAPTCHA Enterprise para orientações sobre o envio do token.
Fontes e referência da API
Execute sua primeira tarefa.
Comece com $5 em créditos no espaço de trabalho.