diff --git a/Config.toml.example b/Config.toml.example index 7bf9e41..3b1acf0 100644 --- a/Config.toml.example +++ b/Config.toml.example @@ -1,10 +1,9 @@ input_path = "keys.txt" -output_path = "output_keys.txt" backup_path = "backup_keys.txt" api_host = "https://generativelanguage.googleapis.com/" timeout_sec = 20 max_retries = 2 -concurrency = 30 +concurrency = 50 proxy = "http://username:password@host:port" enable_multiplexing = true log_level = "info" diff --git a/README.md b/README.md index 2f4f4c4..7f5f833 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ A high-performance tool to validate Google Gemini API keys with batch processing ### Download Pre-built Binaries -Download the latest release from [GitHub Releases](https://github.com/your-username/Gemini-Keychecker/releases): +Download the latest release from [GitHub Releases](https://github.com/Yoo1tic/Gemini-Keychecker/releases): - **Linux**: `gemini-keychecker-linux-x86_64` - **Windows**: `gemini-keychecker-windows-x86_64.exe` @@ -22,7 +22,7 @@ Download the latest release from [GitHub Releases](https://github.com/your-usern ### Build from Source ```bash -git clone https://github.com/your-username/Gemini-Keychecker.git +git clone https://github.com/Yoo1tic/Gemini-Keychecker.git cd Gemini-Keychecker cargo build --release ``` @@ -39,6 +39,7 @@ cargo build --release ``` The tool will: + - Validate all API keys and create output files for valid keys - Generate a backup file (`backup_keys.txt`) containing all processed keys @@ -53,7 +54,6 @@ The tool supports three configuration methods (in order of precedence): Create a `Config.toml` file in the same directory. See `Config.toml.example` for reference. - ### Command Line Options ```bash @@ -68,10 +68,10 @@ Options: -h, --help Print help ``` - ### Examples #### Basic Usage + ```bash # Use default settings ./gemini-keychecker @@ -84,6 +84,7 @@ Options: ``` #### Performance Tuning + ```bash # High concurrency for fast validation ./gemini-keychecker -c 100 -t 10 @@ -93,6 +94,7 @@ Options: ``` #### Proxy Configuration + ```bash # HTTP proxy without authentication ./gemini-keychecker -x http://proxy.company.com:8080 diff --git a/src/adapters/input/local.rs b/src/adapters/input/local.rs index b959653..bd65509 100644 --- a/src/adapters/input/local.rs +++ b/src/adapters/input/local.rs @@ -1,36 +1,36 @@ +use tracing::{error, warn}; + use crate::adapters::output::write_keys_to_file; +use crate::error::ValidatorError; use crate::types::GeminiKey; -use crate::error::Result; use std::{collections::HashSet, fs, path::Path, str::FromStr}; -/// Load and validate API keys from a file -/// Returns a vector of unique, valid API keys -pub fn load_keys(path: &Path) -> Result> { +pub fn load_keys_from_txt(path: &Path) -> Result, ValidatorError> { let keys_txt = fs::read_to_string(path)?; - // Use HashSet to automatically deduplicate keys - let unique_keys_set: HashSet<&str> = keys_txt + + let keys: Vec = keys_txt .lines() - .map(|line| line.trim()) - .filter(|line| !line.is_empty()) + .filter_map(|line| { + let trimmed = line.trim(); + (!trimmed.is_empty()).then_some(trimmed) + }) + .collect::>() + .into_iter() + .filter_map(|key_str| match GeminiKey::from_str(key_str) { + Ok(api_key) => Some(api_key), + Err(e) => { + warn!("Skipping invalid key : {e}"); + None + } + }) .collect(); - let mut keys = Vec::new(); - let mut valid_keys_for_backup = Vec::new(); - - for key_str in unique_keys_set { - match GeminiKey::from_str(key_str) { - Ok(api_key) => { - keys.push(api_key.clone()); - valid_keys_for_backup.push(api_key.inner.clone()); - } - Err(e) => eprintln!("Skipping invalid key: {}", e), + if !keys.is_empty() { + let valid_keys_for_backup: Vec = keys.iter().map(|k| k.inner.clone()).collect(); + if let Err(e) = write_keys_to_file(&valid_keys_for_backup, "backup.txt") { + error!("Failed to write backup file: {e}"); } } - // Write validated keys to backup.txt - if let Err(e) = write_keys_to_file(&valid_keys_for_backup, "backup.txt") { - eprintln!("Failed to write backup file: {}", e); - } - Ok(keys) } diff --git a/src/adapters/mod.rs b/src/adapters/mod.rs index 7d10128..2a50a7c 100644 --- a/src/adapters/mod.rs +++ b/src/adapters/mod.rs @@ -1,5 +1,5 @@ pub mod input; pub mod output; -pub use input::load_keys; -pub use output::write_validated_key_to_tier_files; \ No newline at end of file +pub use input::load_keys_from_txt; +pub use output::write_validated_key_to_tier_files; diff --git a/src/adapters/output/local.rs b/src/adapters/output/local.rs index 85fc849..2edb694 100644 --- a/src/adapters/output/local.rs +++ b/src/adapters/output/local.rs @@ -1,5 +1,5 @@ -use crate::types::{GeminiKey, ValidatedKey, KeyTier}; -use crate::error::Result; +use crate::error::ValidatorError; +use crate::types::{GeminiKey, KeyTier, ValidatedKey}; use std::{fs, io::Write}; use tokio::io::{AsyncWriteExt, BufWriter}; use toml::Value; @@ -10,20 +10,27 @@ pub async fn write_validated_key_to_tier_files( free_file: &mut BufWriter, paid_file: &mut BufWriter, validated_key: &ValidatedKey, -) -> Result<()> { +) -> Result<(), ValidatorError> { match validated_key.tier { KeyTier::Free => { - free_file.write_all(format!("{}\n", validated_key.key.as_ref()).as_bytes()).await?; + free_file + .write_all(format!("{}\n", validated_key.key.as_ref()).as_bytes()) + .await?; } KeyTier::Paid => { - paid_file.write_all(format!("{}\n", validated_key.key.as_ref()).as_bytes()).await?; + paid_file + .write_all(format!("{}\n", validated_key.key.as_ref()).as_bytes()) + .await?; } } Ok(()) } // Write valid key to output file in Clewdr format -pub fn write_keys_clewdr_format(file: &mut fs::File, key: &GeminiKey) -> Result<()> { +pub fn write_keys_clewdr_format( + file: &mut fs::File, + key: &GeminiKey, +) -> Result<(), ValidatorError> { let mut table = toml::value::Table::new(); table.insert("key".to_string(), Value::String(key.as_ref().to_string())); @@ -37,7 +44,7 @@ pub fn write_keys_clewdr_format(file: &mut fs::File, key: &GeminiKey) -> Result< } // Write keys to a text file with custom filename -pub fn write_keys_to_file(keys: &[String], filename: &str) -> Result<()> { +pub fn write_keys_to_file(keys: &[String], filename: &str) -> Result<(), ValidatorError> { let content = keys.join("\n"); fs::write(filename, content)?; info!("File '{}' created with {} keys", filename, keys.len()); diff --git a/src/config/config.rs b/src/config/config.rs index 455deeb..863efda 100644 --- a/src/config/config.rs +++ b/src/config/config.rs @@ -18,7 +18,6 @@ struct Cli { #[serde(skip_serializing_if = "Option::is_none")] input_path: Option, - #[arg(short = 'b', long)] #[serde(skip_serializing_if = "Option::is_none")] backup_path: Option, @@ -50,7 +49,6 @@ pub struct KeyCheckerConfig { #[serde(default)] pub input_path: PathBuf, - // Backup file path for all API keys. #[serde(default)] pub backup_path: PathBuf, diff --git a/src/validation/validation_service.rs b/src/validation/validation_service.rs index 1e183d5..050a046 100644 --- a/src/validation/validation_service.rs +++ b/src/validation/validation_service.rs @@ -1,5 +1,5 @@ use super::key_validator::{test_cache_content_api, test_generate_content_api}; -use crate::adapters::{load_keys, write_validated_key_to_tier_files}; +use crate::adapters::{load_keys_from_txt, write_validated_key_to_tier_files}; use crate::config::KeyCheckerConfig; use crate::error::ValidatorError; use crate::types::GeminiKey; @@ -102,7 +102,7 @@ pub async fn start_validation() -> Result<(), ValidatorError> { let config = KeyCheckerConfig::load_config()?; // 加载密钥 - let keys = load_keys(config.input_path.as_path())?; + let keys = load_keys_from_txt(config.input_path.as_path())?; // 构建HTTP客户端 let client = client_builder(&config)?;