feat: update validation service to handle key writing. output invalid keys to different files.

main
Yoo1tic 2025-08-19 19:50:09 +08:00
parent f126073ce7
commit dbe2d553da
6 changed files with 92 additions and 60 deletions

View File

@ -2,4 +2,4 @@ pub mod input;
pub mod output; pub mod output;
pub use input::load_keys_from_txt; pub use input::load_keys_from_txt;
pub use output::write_validated_key_to_tier_writers;

View File

@ -1,34 +1,9 @@
use crate::error::ValidatorError; use crate::error::ValidatorError;
use crate::types::{GeminiKey, KeyTier, ValidatedKey}; use crate::types::GeminiKey;
use std::{fs, io::Write}; use std::{fs, io::Write};
use tokio::io::{AsyncWrite, AsyncWriteExt};
use toml::Value; use toml::Value;
use tracing::info; use tracing::info;
// Write valid key to appropriate tier file
pub async fn write_validated_key_to_tier_writers<W>(
free_writer: &mut W,
paid_writer: &mut W,
validated_key: &ValidatedKey,
) -> Result<(), ValidatorError>
where
W: AsyncWrite + Unpin,
{
match validated_key.tier {
KeyTier::Free => {
free_writer
.write_all(format!("{}\n", validated_key.key.as_ref()).as_bytes())
.await?;
}
KeyTier::Paid => {
paid_writer
.write_all(format!("{}\n", validated_key.key.as_ref()).as_bytes())
.await?;
}
}
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( pub fn write_keys_clewdr_format(
file: &mut fs::File, file: &mut fs::File,

View File

@ -1,3 +1,5 @@
pub mod http; pub mod http;
pub mod writer;
pub use http::{client_builder, send_request}; pub use http::{client_builder, send_request};
pub use writer::write_key_into_file;

16
src/utils/writer.rs Normal file
View File

@ -0,0 +1,16 @@
use crate::error::ValidatorError;
use crate::types::ValidatedKey;
use tokio::io::{AsyncWrite, AsyncWriteExt};
pub async fn write_key_into_file<W>(
writer: &mut W,
validated_key: &ValidatedKey,
) -> Result<(), ValidatorError>
where
W: AsyncWrite + Unpin,
{
writer
.write_all(format!("{}\n", validated_key.key.as_ref()).as_bytes())
.await?;
Ok(())
}

View File

@ -12,7 +12,7 @@ pub async fn test_generate_content_api(
api_endpoint: impl IntoUrl, api_endpoint: impl IntoUrl,
api_key: GeminiKey, api_key: GeminiKey,
config: KeyCheckerConfig, config: KeyCheckerConfig,
) -> Result<ValidatedKey, ValidatorError> { ) -> Result<(), ValidatorError> {
let api_endpoint = api_endpoint.into_url().unwrap(); let api_endpoint = api_endpoint.into_url().unwrap();
match send_request( match send_request(
@ -29,7 +29,7 @@ pub async fn test_generate_content_api(
"BASIC API VALID - {}... - Passed generate content API test", "BASIC API VALID - {}... - Passed generate content API test",
&api_key.as_ref()[..10] &api_key.as_ref()[..10]
); );
Ok(ValidatedKey::new(api_key)) Ok(())
} }
Err(e) => match &e { Err(e) => match &e {
ValidatorError::HttpBadRequest { .. } ValidatorError::HttpBadRequest { .. }

View File

@ -1,9 +1,9 @@
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_from_txt, write_validated_key_to_tier_writers}; use crate::adapters::load_keys_from_txt;
use crate::config::KeyCheckerConfig; use crate::config::KeyCheckerConfig;
use crate::error::ValidatorError; use crate::error::ValidatorError;
use crate::types::GeminiKey; use crate::types::{GeminiKey, KeyTier, ValidatedKey};
use crate::utils::client_builder; use crate::utils::{client_builder, write_key_into_file};
use async_stream::stream; use async_stream::stream;
use futures::{pin_mut, stream::StreamExt}; use futures::{pin_mut, stream::StreamExt};
use indicatif::ProgressStyle; use indicatif::ProgressStyle;
@ -30,6 +30,19 @@ impl ValidationService {
pub async fn validate_keys(&self, keys: Vec<GeminiKey>) -> Result<(), ValidatorError> { pub async fn validate_keys(&self, keys: Vec<GeminiKey>) -> Result<(), ValidatorError> {
let total_keys = keys.len(); let total_keys = keys.len();
// Create a progress bar to track validation progress
let progress_span = info_span!("key_checker");
progress_span.pb_set_style(
&ProgressStyle::with_template(
"[{bar:60.cyan/blue}] {pos}/{len} ({percent}%) [{elapsed_precise}] ETA:{eta} Speed:{per_sec}",
)
.unwrap(),
);
progress_span.pb_set_length(total_keys as u64);
progress_span.pb_set_message("Validating keys...");
progress_span.pb_set_finish_message("All items processed");
let progress_span_enter = progress_span.enter();
// Create channel for streaming keys from producer to consumer // Create channel for streaming keys from producer to consumer
let (tx, mut rx) = mpsc::unbounded_channel::<GeminiKey>(); let (tx, mut rx) = mpsc::unbounded_channel::<GeminiKey>();
let stream = stream! { let stream = stream! {
@ -46,35 +59,38 @@ impl ValidationService {
} }
} }
}); });
// Create a progress bar to track validation progress
let progress_span = info_span!("key_checker");
progress_span.pb_set_style(
&ProgressStyle::with_template(
"[{bar:60.cyan/blue}] {pos}/{len} ({percent}%) [{elapsed_precise}] ETA:{eta} Speed:{per_sec}",
)
.unwrap(),
);
progress_span.pb_set_length(total_keys as u64);
progress_span.pb_set_message("Validating keys...");
progress_span.pb_set_finish_message("All items processed");
let progress_span_enter = progress_span.enter();
// Create stream to validate keys concurrently (two-stage pipeline) let (invalid_tx, mut invalid_rx) = mpsc::unbounded_channel::<GeminiKey>();
let invalid_stream = stream! {
while let Some(item) = invalid_rx.recv().await {
yield ValidatedKey::new(item);
}
};
// Create invalid keys stream
let cache_api_url = self.config.cache_api_url(); let cache_api_url = self.config.cache_api_url();
let valid_keys_stream = stream let valid_keys_stream = stream
.map(|key| async move { .map(|key| async move {
let result = test_generate_content_api( let result = test_generate_content_api(
self.client.clone(), self.client.clone(),
self.full_url.clone(), self.full_url.clone(),
key, key.clone(),
self.config.clone(), self.config.clone(),
) )
.await; .await;
Span::current().pb_inc(1); Span::current().pb_inc(1);
result (key, result)
}) })
.buffer_unordered(self.config.concurrency) .buffer_unordered(self.config.concurrency)
.filter_map(|result| async { result.ok() }) .filter_map(|(key, result)| async {
match result {
Ok(()) => Some(ValidatedKey::new(key)),
Err(_e) => {
let _ = invalid_tx.send(key);
None
}
}
})
.map(|validated_key| { .map(|validated_key| {
test_cache_content_api(self.client.clone(), cache_api_url.clone(), validated_key) test_cache_content_api(self.client.clone(), cache_api_url.clone(), validated_key)
}) })
@ -84,27 +100,50 @@ impl ValidationService {
// Open output files for writing keys by tier (fixed filenames) // Open output files for writing keys by tier (fixed filenames)
let free_keys_path = "freekey.txt"; let free_keys_path = "freekey.txt";
let paid_keys_path = "paidkey.txt"; let paid_keys_path = "paidkey.txt";
let invalid_keys_path = "invalidkey.txt";
let free_file = fs::File::create(&free_keys_path).await?; let free_file = fs::File::create(&free_keys_path).await?;
let paid_file = fs::File::create(&paid_keys_path).await?; let paid_file = fs::File::create(&paid_keys_path).await?;
let invalid_file = fs::File::create(&invalid_keys_path).await?;
let mut free_buffer_writer = tokio::io::BufWriter::new(free_file); let mut free_buffer_writer = tokio::io::BufWriter::new(free_file);
let mut paid_buffer_writer = tokio::io::BufWriter::new(paid_file); let mut paid_buffer_writer = tokio::io::BufWriter::new(paid_file);
let mut invalid_buffer_writer = tokio::io::BufWriter::new(invalid_file);
// Process validated keys and write to appropriate tier files // Spawn task to process invalid keys stream
while let Some(valid_key) = valid_keys_stream.next().await { tokio::spawn(async move {
if let Err(e) = write_validated_key_to_tier_writers( let mut pinned_stream = Box::pin(invalid_stream);
&mut free_buffer_writer, while let Some(invalid_key) = pinned_stream.next().await {
&mut paid_buffer_writer, if let Err(e) = write_key_into_file(&mut invalid_buffer_writer, &invalid_key).await
&valid_key, {
) error!("Failed to write invalid key to file: {e}");
.await }
{ }
error!("Failed to write key to output file: {e}"); if let Err(e) = invalid_buffer_writer.flush().await {
error!("Failed to flush invalid keys buffer: {e}");
}
});
// Process all keys and write to appropriate tier files
while let Some(validated_key) = valid_keys_stream.next().await {
match validated_key.tier {
KeyTier::Free => {
if let Err(e) =
write_key_into_file(&mut free_buffer_writer, &validated_key).await
{
error!("Failed to write free key to file: {e}");
}
}
KeyTier::Paid => {
if let Err(e) =
write_key_into_file(&mut paid_buffer_writer, &validated_key).await
{
error!("Failed to write paid key to file: {e}");
}
}
} }
} }
// Flush buffers for valid keys
// Flush both buffers to ensure all data is written to files
free_buffer_writer.flush().await?; free_buffer_writer.flush().await?;
paid_buffer_writer.flush().await?; paid_buffer_writer.flush().await?;