fix: update function names and improve key loading logic in adapters

main
Yoo1tic 2025-08-09 07:50:02 +08:00
parent 3fd7bf0b00
commit d7ef92684e
7 changed files with 48 additions and 42 deletions

View File

@ -1,10 +1,9 @@
input_path = "keys.txt" input_path = "keys.txt"
output_path = "output_keys.txt"
backup_path = "backup_keys.txt" backup_path = "backup_keys.txt"
api_host = "https://generativelanguage.googleapis.com/" api_host = "https://generativelanguage.googleapis.com/"
timeout_sec = 20 timeout_sec = 20
max_retries = 2 max_retries = 2
concurrency = 30 concurrency = 50
proxy = "http://username:password@host:port" proxy = "http://username:password@host:port"
enable_multiplexing = true enable_multiplexing = true
log_level = "info" log_level = "info"

View File

@ -14,7 +14,7 @@ A high-performance tool to validate Google Gemini API keys with batch processing
### Download Pre-built Binaries ### 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` - **Linux**: `gemini-keychecker-linux-x86_64`
- **Windows**: `gemini-keychecker-windows-x86_64.exe` - **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 ### Build from Source
```bash ```bash
git clone https://github.com/your-username/Gemini-Keychecker.git git clone https://github.com/Yoo1tic/Gemini-Keychecker.git
cd Gemini-Keychecker cd Gemini-Keychecker
cargo build --release cargo build --release
``` ```
@ -39,6 +39,7 @@ cargo build --release
``` ```
The tool will: The tool will:
- Validate all API keys and create output files for valid keys - Validate all API keys and create output files for valid keys
- Generate a backup file (`backup_keys.txt`) containing all processed 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. Create a `Config.toml` file in the same directory. See `Config.toml.example` for reference.
### Command Line Options ### Command Line Options
```bash ```bash
@ -68,10 +68,10 @@ Options:
-h, --help Print help -h, --help Print help
``` ```
### Examples ### Examples
#### Basic Usage #### Basic Usage
```bash ```bash
# Use default settings # Use default settings
./gemini-keychecker ./gemini-keychecker
@ -84,6 +84,7 @@ Options:
``` ```
#### Performance Tuning #### Performance Tuning
```bash ```bash
# High concurrency for fast validation # High concurrency for fast validation
./gemini-keychecker -c 100 -t 10 ./gemini-keychecker -c 100 -t 10
@ -93,6 +94,7 @@ Options:
``` ```
#### Proxy Configuration #### Proxy Configuration
```bash ```bash
# HTTP proxy without authentication # HTTP proxy without authentication
./gemini-keychecker -x http://proxy.company.com:8080 ./gemini-keychecker -x http://proxy.company.com:8080

View File

@ -1,36 +1,36 @@
use tracing::{error, warn};
use crate::adapters::output::write_keys_to_file; use crate::adapters::output::write_keys_to_file;
use crate::error::ValidatorError;
use crate::types::GeminiKey; use crate::types::GeminiKey;
use crate::error::Result;
use std::{collections::HashSet, fs, path::Path, str::FromStr}; use std::{collections::HashSet, fs, path::Path, str::FromStr};
/// Load and validate API keys from a file pub fn load_keys_from_txt(path: &Path) -> Result<Vec<GeminiKey>, ValidatorError> {
/// Returns a vector of unique, valid API keys
pub fn load_keys(path: &Path) -> Result<Vec<GeminiKey>> {
let keys_txt = fs::read_to_string(path)?; 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<GeminiKey> = keys_txt
.lines() .lines()
.map(|line| line.trim()) .filter_map(|line| {
.filter(|line| !line.is_empty()) let trimmed = line.trim();
(!trimmed.is_empty()).then_some(trimmed)
})
.collect::<HashSet<_>>()
.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(); .collect();
let mut keys = Vec::new(); if !keys.is_empty() {
let mut valid_keys_for_backup = Vec::new(); let valid_keys_for_backup: Vec<String> = keys.iter().map(|k| k.inner.clone()).collect();
if let Err(e) = write_keys_to_file(&valid_keys_for_backup, "backup.txt") {
for key_str in unique_keys_set { error!("Failed to write backup file: {e}");
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),
} }
} }
// 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) Ok(keys)
} }

View File

@ -1,5 +1,5 @@
pub mod input; pub mod input;
pub mod output; pub mod output;
pub use input::load_keys; pub use input::load_keys_from_txt;
pub use output::write_validated_key_to_tier_files; pub use output::write_validated_key_to_tier_files;

View File

@ -1,5 +1,5 @@
use crate::types::{GeminiKey, ValidatedKey, KeyTier}; use crate::error::ValidatorError;
use crate::error::Result; use crate::types::{GeminiKey, KeyTier, ValidatedKey};
use std::{fs, io::Write}; use std::{fs, io::Write};
use tokio::io::{AsyncWriteExt, BufWriter}; use tokio::io::{AsyncWriteExt, BufWriter};
use toml::Value; use toml::Value;
@ -10,20 +10,27 @@ pub async fn write_validated_key_to_tier_files(
free_file: &mut BufWriter<tokio::fs::File>, free_file: &mut BufWriter<tokio::fs::File>,
paid_file: &mut BufWriter<tokio::fs::File>, paid_file: &mut BufWriter<tokio::fs::File>,
validated_key: &ValidatedKey, validated_key: &ValidatedKey,
) -> Result<()> { ) -> Result<(), ValidatorError> {
match validated_key.tier { match validated_key.tier {
KeyTier::Free => { 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 => { 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(()) Ok(())
} }
// Write valid key to output file in Clewdr format // 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(); let mut table = toml::value::Table::new();
table.insert("key".to_string(), Value::String(key.as_ref().to_string())); 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 // 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"); let content = keys.join("\n");
fs::write(filename, content)?; fs::write(filename, content)?;
info!("File '{}' created with {} keys", filename, keys.len()); info!("File '{}' created with {} keys", filename, keys.len());

View File

@ -18,7 +18,6 @@ struct Cli {
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
input_path: Option<PathBuf>, input_path: Option<PathBuf>,
#[arg(short = 'b', long)] #[arg(short = 'b', long)]
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
backup_path: Option<PathBuf>, backup_path: Option<PathBuf>,
@ -50,7 +49,6 @@ pub struct KeyCheckerConfig {
#[serde(default)] #[serde(default)]
pub input_path: PathBuf, pub input_path: PathBuf,
// Backup file path for all API keys. // Backup file path for all API keys.
#[serde(default)] #[serde(default)]
pub backup_path: PathBuf, pub backup_path: PathBuf,

View File

@ -1,5 +1,5 @@
use super::key_validator::{test_cache_content_api, test_generate_content_api}; 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::config::KeyCheckerConfig;
use crate::error::ValidatorError; use crate::error::ValidatorError;
use crate::types::GeminiKey; use crate::types::GeminiKey;
@ -102,7 +102,7 @@ pub async fn start_validation() -> Result<(), ValidatorError> {
let config = KeyCheckerConfig::load_config()?; 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客户端 // 构建HTTP客户端
let client = client_builder(&config)?; let client = client_builder(&config)?;