From d8b9a064533ac21f2de645b066217f5595007714 Mon Sep 17 00:00:00 2001 From: YoursFunny Date: Thu, 24 Sep 2026 22:36:33 +0800 Subject: [PATCH] fix: bound direct command fetches --- crates/x-media/src/site/mod.rs | 11 +++++++++++ crates/xmedia-bot/src/handlers/commands.rs | 10 +++------- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/crates/x-media/src/site/mod.rs b/crates/x-media/src/site/mod.rs index 790bdec..f834801 100644 --- a/crates/x-media/src/site/mod.rs +++ b/crates/x-media/src/site/mod.rs @@ -516,6 +516,15 @@ pub async fn fetch_once(url: &str) -> Result, FetchError> { fetch_with_attempts(url, 1).await } +/// A small admission gate for `/test` and `/debug`, which run directly in +/// dispatcher handlers instead of the URL worker pool. +pub async fn acquire_command_fetch_slot() -> tokio::sync::OwnedSemaphorePermit { + std::sync::Arc::clone(&COMMAND_FETCH_SLOTS) + .acquire_owned() + .await + .expect("command fetch gate closed") +} + /// Total attempts of the retried [`fetch`] (3: the initial try plus two). const MAX_FETCH_ATTEMPTS: u32 = 3; @@ -530,6 +539,8 @@ const MAX_FETCH_ATTEMPTS: u32 = 3; /// deliberately simple: the wait is bounded by the same retries. static FETCH_SLOTS: LazyLock = LazyLock::new(|| tokio::sync::Semaphore::new(8)); +static COMMAND_FETCH_SLOTS: LazyLock> = + LazyLock::new(|| std::sync::Arc::new(tokio::sync::Semaphore::new(2))); async fn fetch_with_attempts(url: &str, attempts: u32) -> Result, FetchError> { // Wall time of the whole fetch, retry backoff included: the ugoira encode diff --git a/crates/xmedia-bot/src/handlers/commands.rs b/crates/xmedia-bot/src/handlers/commands.rs index d4ef65d..e4028f8 100644 --- a/crates/xmedia-bot/src/handlers/commands.rs +++ b/crates/xmedia-bot/src/handlers/commands.rs @@ -598,11 +598,7 @@ pub(crate) async fn execute_command( .await?; return Ok(()); } - // The ordinary link pipeline with the chat's post-send actions - // suppressed: the media is sent (and cached) like a normal link, - // but nothing is forwarded to the channel and no - // edit-before-forward prompt opens. Info level echoes the - // normalized key (never the raw URL) per the logging convention. + let _command_fetch = x_media::site::acquire_command_fetch_slot().await; log::info!("test: sending [key={}]", log_key(url)); url_media( ctx, @@ -612,6 +608,7 @@ pub(crate) async fn execute_command( PostSend::Suppressed, ) .await; + drop(_command_fetch); } Command::Debug(arg) => { let url = arg.trim(); @@ -625,8 +622,7 @@ pub(crate) async fn execute_command( .await?; return Ok(()); } - // Debug tool: report the parse result only — nothing is sent, - // cached or forwarded. + let _command_fetch = x_media::site::acquire_command_fetch_slot().await; log::info!("debug: parsing [key={}]", log_key(url)); match x_media::site::fetch(url).await { Ok(None) => {