Compare commits

..

No commits in common. "1bc2990aa6ee82c2079a3bfe21a509db8f2e479a" and "4a8ca4cd220742328a7fbf6f7ec2e62340936bdb" have entirely different histories.

13 changed files with 119 additions and 156 deletions

View File

@ -42,13 +42,13 @@ jobs:
- name: Package artifact - name: Package artifact
run: | run: |
mv target/${{ matrix.target_triple }}/release/${{ matrix.artifact_name }} ${{ matrix.artifact_name }} mv target/${{ matrix.target_triple }}/release/${{ matrix.artifact_name }} ${{ matrix.release_name }}
- name: Upload artifact - name: Upload artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: ${{ matrix.release_name }} name: ${{ matrix.release_name }}
path: ${{ matrix.artifact_name }} path: ${{ matrix.release_name }}
retention-days: 30 retention-days: 30
windows: windows:
@ -71,13 +71,14 @@ jobs:
- name: Package artifact - name: Package artifact
run: | run: |
mv target/${{ matrix.target_triple }}/release/${{ matrix.artifact_name }} ${{ matrix.artifact_name }} mkdir -p release-package
mv target/${{ matrix.target_triple }}/release/${{ matrix.artifact_name }} ${{ matrix.release_name }}
- name: Upload artifact - name: Upload artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: ${{ matrix.release_name }} name: ${{ matrix.release_name }}
path: ${{ matrix.artifact_name }} path: ${{ matrix.release_name }}
retention-days: 30 retention-days: 30
macos: macos:
@ -107,11 +108,11 @@ jobs:
- name: Package artifact - name: Package artifact
run: | run: |
mv target/${{ matrix.target_triple }}/release/${{ matrix.artifact_name }} ${{ matrix.artifact_name }} mv target/${{ matrix.target_triple }}/release/${{ matrix.artifact_name }} ${{ matrix.release_name }}
- name: Upload artifact - name: Upload artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: ${{ matrix.release_name }} name: ${{ matrix.release_name }}
path: ${{ matrix.artifact_name }} path: ${{ matrix.release_name }}
retention-days: 30 retention-days: 30

79
Cargo.lock generated
View File

@ -177,9 +177,9 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.2.30" version = "1.2.29"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "deec109607ca693028562ed836a5f1c4b8bd77755c4e132fc5ce11b0b6211ae7" checksum = "5c1599538de2394445747c8cf7935946e3cc27e9625f889d979bfb2aaf569362"
dependencies = [ dependencies = [
"shlex", "shlex",
] ]
@ -198,9 +198,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.5.41" version = "4.5.40"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be92d32e80243a54711e5d7ce823c35c41c9d929dc4ab58e1276f625841aadf9" checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
"clap_derive", "clap_derive",
@ -208,9 +208,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_builder" name = "clap_builder"
version = "4.5.41" version = "4.5.40"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "707eab41e9622f9139419d573eca0900137718000c517d47da73045f54331c3d" checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e"
dependencies = [ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
@ -220,9 +220,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_derive" name = "clap_derive"
version = "4.5.41" version = "4.5.40"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491" checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce"
dependencies = [ dependencies = [
"heck", "heck",
"proc-macro2", "proc-macro2",
@ -404,7 +404,7 @@ dependencies = [
[[package]] [[package]]
name = "gemini-keychecker" name = "gemini-keychecker"
version = "0.3.0" version = "0.2.0"
dependencies = [ dependencies = [
"async-stream", "async-stream",
"backon", "backon",
@ -419,7 +419,7 @@ dependencies = [
"serde_json", "serde_json",
"thiserror", "thiserror",
"tokio", "tokio",
"toml 0.9.2", "toml 0.9.5",
"tracing", "tracing",
"tracing-indicatif", "tracing-indicatif",
"tracing-subscriber", "tracing-subscriber",
@ -581,9 +581,9 @@ dependencies = [
[[package]] [[package]]
name = "hyper-util" name = "hyper-util"
version = "0.1.16" version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e" checksum = "dc2fdfdbff08affe55bb779f33b053aa1fe5dd5b54c257343c17edfa55711bdb"
dependencies = [ dependencies = [
"base64", "base64",
"bytes", "bytes",
@ -597,7 +597,7 @@ dependencies = [
"libc", "libc",
"percent-encoding", "percent-encoding",
"pin-project-lite", "pin-project-lite",
"socket2 0.6.0", "socket2 0.5.10",
"tokio", "tokio",
"tower-service", "tower-service",
"tracing", "tracing",
@ -742,9 +742,9 @@ checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb"
[[package]] [[package]]
name = "io-uring" name = "io-uring"
version = "0.7.9" version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4" checksum = "b86e202f00093dcba4275d4636b93ef9dd75d025ae560d2521b45ea28ab49013"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"cfg-if", "cfg-if",
@ -1069,9 +1069,9 @@ checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]] [[package]]
name = "rand" name = "rand"
version = "0.9.2" version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97"
dependencies = [ dependencies = [
"rand_chacha", "rand_chacha",
"rand_core", "rand_core",
@ -1207,9 +1207,9 @@ checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
[[package]] [[package]]
name = "rustls" name = "rustls"
version = "0.23.29" version = "0.23.28"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2491382039b29b9b11ff08b76ff6c97cf287671dbb74f0be44bda389fffe9bd1" checksum = "7160e3e10bf4535308537f3c4e1641468cd0e485175d6163087c0393c7d46643"
dependencies = [ dependencies = [
"once_cell", "once_cell",
"ring", "ring",
@ -1231,9 +1231,9 @@ dependencies = [
[[package]] [[package]]
name = "rustls-webpki" name = "rustls-webpki"
version = "0.103.4" version = "0.103.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435"
dependencies = [ dependencies = [
"ring", "ring",
"rustls-pki-types", "rustls-pki-types",
@ -1274,9 +1274,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.141" version = "1.0.140"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30b9eff21ebe718216c6ec64e1d9ac57087aad11efc64e32002bce4a0d4c03d3" checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
dependencies = [ dependencies = [
"itoa", "itoa",
"memchr", "memchr",
@ -1466,9 +1466,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]] [[package]]
name = "tokio" name = "tokio"
version = "1.46.1" version = "1.47.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cc3a2344dafbe23a245241fe8b09735b521110d30fcefbbd5feb1797ca35d17" checksum = "43864ed400b6043a4757a25c7a64a8efde741aed79a056a2fb348a406701bb35"
dependencies = [ dependencies = [
"backtrace", "backtrace",
"bytes", "bytes",
@ -1477,9 +1477,9 @@ dependencies = [
"mio", "mio",
"pin-project-lite", "pin-project-lite",
"slab", "slab",
"socket2 0.5.10", "socket2 0.6.0",
"tokio-macros", "tokio-macros",
"windows-sys 0.52.0", "windows-sys 0.59.0",
] ]
[[package]] [[package]]
@ -1530,9 +1530,9 @@ dependencies = [
[[package]] [[package]]
name = "toml" name = "toml"
version = "0.9.2" version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed0aee96c12fa71097902e0bb061a5e1ebd766a6636bb605ba401c45c1650eac" checksum = "75129e1dc5000bfbaa9fee9d1b21f974f9fbad9daec557a521ee6e080825f6e8"
dependencies = [ dependencies = [
"indexmap", "indexmap",
"serde", "serde",
@ -1577,9 +1577,9 @@ dependencies = [
[[package]] [[package]]
name = "toml_parser" name = "toml_parser"
version = "1.0.1" version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97200572db069e74c512a14117b296ba0a80a30123fbbb5aa1f4a348f639ca30" checksum = "b551886f449aa90d4fe2bdaa9f4a2577ad2dde302c61ecf262d80b116db95c10"
dependencies = [ dependencies = [
"winnow", "winnow",
] ]
@ -1675,9 +1675,9 @@ dependencies = [
[[package]] [[package]]
name = "tracing-indicatif" name = "tracing-indicatif"
version = "0.3.11" version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c714cc8fc46db04fcfddbd274c6ef59bebb1b435155984e7c6e89c3ce66f200" checksum = "e1983afead46ff13a3c93581e0cec31d20b29efdd22cbdaa8b9f850eccf2c352"
dependencies = [ dependencies = [
"indicatif", "indicatif",
"tracing", "tracing",
@ -1974,6 +1974,12 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-link"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.52.0" version = "0.52.0"
@ -1998,7 +2004,7 @@ version = "0.60.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
dependencies = [ dependencies = [
"windows-targets 0.53.2", "windows-targets 0.53.3",
] ]
[[package]] [[package]]
@ -2019,10 +2025,11 @@ dependencies = [
[[package]] [[package]]
name = "windows-targets" name = "windows-targets"
version = "0.53.2" version = "0.53.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef" checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91"
dependencies = [ dependencies = [
"windows-link",
"windows_aarch64_gnullvm 0.53.0", "windows_aarch64_gnullvm 0.53.0",
"windows_aarch64_msvc 0.53.0", "windows_aarch64_msvc 0.53.0",
"windows_i686_gnu 0.53.0", "windows_i686_gnu 0.53.0",

View File

@ -1,6 +1,6 @@
[package] [package]
name = "gemini-keychecker" name = "gemini-keychecker"
version = "0.3.0" version = "0.2.0"
edition = "2024" edition = "2024"
[dependencies] [dependencies]
@ -11,7 +11,7 @@ futures = "0.3"
regex = "1.11" regex = "1.11"
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls","http2"] } reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls","http2"] }
serde_json = "1.0" serde_json = "1.0"
tokio = { version = "1.46.1", features = [ tokio = { version = "1.47", features = [
"macros", "macros",
"rt-multi-thread", "rt-multi-thread",
"time", "time",
@ -21,9 +21,9 @@ url = { version = "2.5", features = ["serde"] }
async-stream = "0.3" async-stream = "0.3"
figment = { version = "0.10", features = ["env", "toml"] } figment = { version = "0.10", features = ["env", "toml"] }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
toml = "0.9" toml = "0.9.5"
thiserror = "2.0" thiserror = "2.0"
tracing = "0.1" tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] } tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] }
tracing-indicatif = "0.3" tracing-indicatif = "0.3.12"
indicatif = "0.18" indicatif = "0.18.0"

0
release.sh Executable file → Normal file
View File

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_files;

View File

@ -1,9 +1,31 @@
use crate::error::ValidatorError; use crate::error::ValidatorError;
use crate::types::GeminiKey; use crate::types::{GeminiKey, KeyTier, ValidatedKey};
use std::{fs, io::Write}; use std::{fs, io::Write};
use tokio::io::{AsyncWriteExt, BufWriter};
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_files(
free_file: &mut BufWriter<tokio::fs::File>,
paid_file: &mut BufWriter<tokio::fs::File>,
validated_key: &ValidatedKey,
) -> Result<(), ValidatorError> {
match validated_key.tier {
KeyTier::Free => {
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?;
}
}
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

@ -79,9 +79,6 @@ pub struct KeyCheckerConfig {
#[serde(default)] #[serde(default)]
pub log_level: String, pub log_level: String,
#[serde(default)]
pub model: String,
} }
impl Default for KeyCheckerConfig { impl Default for KeyCheckerConfig {
@ -115,9 +112,7 @@ impl KeyCheckerConfig {
/// Returns the complete Gemini API URL for generateContent endpoint /// Returns the complete Gemini API URL for generateContent endpoint
pub fn gemini_api_url(&self) -> Url { pub fn gemini_api_url(&self) -> Url {
self.api_host self.api_host
.join("v1beta/models/") .join("v1beta/models/gemini-2.5-flash-lite:generateContent")
.join(self.model)
.join(":generateContent")
.expect("Failed to join API URL") .expect("Failed to join API URL")
} }
@ -167,7 +162,6 @@ static DEFAULT_CONFIG: LazyLock<KeyCheckerConfig> = LazyLock::new(|| KeyCheckerC
proxy: None, proxy: None,
enable_multiplexing: true, enable_multiplexing: true,
log_level: "info".to_string(), log_level: "info".to_string(),
model: "gemini-2.5-flash-lite".to_string(),
}); });
fn default_api_host() -> Url { fn default_api_host() -> Url {

View File

@ -21,12 +21,7 @@ async fn main() -> Result<(), ValidatorError> {
tracing_subscriber::registry() tracing_subscriber::registry()
.with(env_filter) .with(env_filter)
.with( .with(tracing_subscriber::fmt::layer().with_writer(indicatif_layer.get_stderr_writer()))
tracing_subscriber::fmt::layer()
.with_writer(indicatif_layer.get_stderr_writer())
.with_level(true)
.with_target(false),
)
.with(indicatif_layer) .with(indicatif_layer)
.init(); .init();

View File

@ -1,5 +1,3 @@
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;

View File

@ -1,16 +0,0 @@
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<(), ValidatorError> { ) -> Result<ValidatedKey, 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(()) Ok(ValidatedKey::new(api_key))
} }
Err(e) => match &e { Err(e) => match &e {
ValidatorError::HttpBadRequest { .. } ValidatorError::HttpBadRequest { .. }

View File

@ -64,10 +64,8 @@ pub static GENERATE_CONTENT_TEST_BODY: LazyLock<Value> = LazyLock::new(|| {
// LazyLock for the cached content test body used in cache API validation // LazyLock for the cached content test body used in cache API validation
pub static CACHE_CONTENT_TEST_BODY: LazyLock<GeminiRequest> = LazyLock::new(|| { pub static CACHE_CONTENT_TEST_BODY: LazyLock<GeminiRequest> = LazyLock::new(|| {
// Generate random text content to meet the minimum 2048 tokens requirement for cache API // Generate random text content to meet the minimum 1024 tokens requirement for cache API
// models/gemini-2.5-flash need 1024 tokens let long_text = "You are an expert at analyzing transcripts.".repeat(150);
// models/gemini-2.5-flash-lite need 2048 tokens
let long_text = "You are an expert at analyzing transcripts.".repeat(128);
GeminiRequest { GeminiRequest {
model: Some("models/gemini-2.5-flash".to_string()), model: Some("models/gemini-2.5-flash".to_string()),
contents: vec![ContentPart { contents: vec![ContentPart {

View File

@ -1,13 +1,14 @@
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; 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, KeyTier, ValidatedKey}; use crate::types::GeminiKey;
use crate::utils::{client_builder, write_key_into_file}; use crate::utils::client_builder;
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;
use reqwest::Client; use reqwest::Client;
use std::time::Instant;
use tokio::{fs, io::AsyncWriteExt, sync::mpsc}; use tokio::{fs, io::AsyncWriteExt, sync::mpsc};
use tracing::{Span, error, info_span}; use tracing::{Span, error, info_span};
use tracing_indicatif::span_ext::IndicatifSpanExt; use tracing_indicatif::span_ext::IndicatifSpanExt;
@ -29,20 +30,8 @@ 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 start_time = Instant::now();
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! {
@ -59,38 +48,35 @@ 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();
let (invalid_tx, mut invalid_rx) = mpsc::unbounded_channel::<GeminiKey>(); // Create stream to validate keys concurrently (two-stage pipeline)
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.clone(), key,
self.config.clone(), self.config.clone(),
) )
.await; .await;
Span::current().pb_inc(1); Span::current().pb_inc(1);
(key, result) result
}) })
.buffer_unordered(self.config.concurrency) .buffer_unordered(self.config.concurrency)
.filter_map(|(key, result)| async { .filter_map(|result| async { result.ok() })
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)
}) })
@ -100,56 +86,34 @@ 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);
// Spawn task to process invalid keys stream // Process validated keys and write to appropriate tier files
tokio::spawn(async move { while let Some(valid_key) = valid_keys_stream.next().await {
let mut pinned_stream = Box::pin(invalid_stream); if let Err(e) = write_validated_key_to_tier_files(
while let Some(invalid_key) = pinned_stream.next().await { &mut free_buffer_writer,
if let Err(e) = write_key_into_file(&mut invalid_buffer_writer, &invalid_key).await &mut paid_buffer_writer,
{ &valid_key,
error!("Failed to write invalid key to file: {e}"); )
} .await
} {
if let Err(e) = invalid_buffer_writer.flush().await { error!("Failed to write key to output file: {e}");
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?;
std::mem::drop(progress_span_enter); std::mem::drop(progress_span_enter);
std::mem::drop(progress_span); std::mem::drop(progress_span);
println!("Total Elapsed Time: {:?}", start_time.elapsed());
Ok(()) Ok(())
} }
} }