From 132fdd2999afc74f34f68a8598b44ab1ccfb00f3 Mon Sep 17 00:00:00 2001 From: YoursFunny Date: Sat, 31 Oct 2026 18:39:12 +0800 Subject: [PATCH] fix: reply the edit-before-forward prompt to the post's own media message One user message carrying several links opens one prompt per post; every prompt used to reply to that shared link message, so they were indistinguishable. The prompt now replies to the first media message it edits, falling back to the link message when there is none. --- crates/xmedia-bot/src/media_sender/test_support.rs | 14 +++++++++++++- crates/xmedia-bot/src/send/mod.rs | 3 +++ crates/xmedia-bot/src/send/post_send.rs | 8 +++++++- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/crates/xmedia-bot/src/media_sender/test_support.rs b/crates/xmedia-bot/src/media_sender/test_support.rs index 19883b7..28a6156 100644 --- a/crates/xmedia-bot/src/media_sender/test_support.rs +++ b/crates/xmedia-bot/src/media_sender/test_support.rs @@ -227,6 +227,8 @@ pub(crate) struct MockSender { cursor: Mutex, calls: Mutex>, messages: Mutex>, + /// The reply target of every `send_message`, in order. + message_replies: Mutex>>, captions: Mutex>, answers: Mutex>>, /// `(chat, message, text)` of every text rewrite, in order. @@ -281,6 +283,7 @@ impl MockSender { cursor: Mutex::new(0), calls: Mutex::new(Vec::new()), messages: Mutex::new(Vec::new()), + message_replies: Mutex::new(Vec::new()), captions: Mutex::new(Vec::new()), answers: Mutex::new(Vec::new()), edited_texts: Mutex::new(Vec::new()), @@ -301,6 +304,12 @@ impl MockSender { self.messages.lock().clone() } + /// The reply target of every plain message sent, in order (`None` = no + /// reply). + pub(crate) fn message_replies(&self) -> Vec> { + self.message_replies.lock().clone() + } + /// Captions passed to `edit_message_caption`, in order. pub(crate) fn captions(&self) -> Vec { self.captions.lock().clone() @@ -415,11 +424,14 @@ impl MediaSender for MockSender { &self, _chat_id: ChatId, text: String, - _reply_to: Option, + reply_to: Option, _reply_markup: Option, ) -> BoxFuture<'_, Result> { Box::pin(async move { self.messages.lock().push(text); + self.message_replies + .lock() + .push(reply_to.map(|id| id.0 as i64)); match self.next("send_message") { Outcome::MessageOk => Ok(MockSender::SENT_ID), Outcome::MessageErr => Err(self.error()), diff --git a/crates/xmedia-bot/src/send/mod.rs b/crates/xmedia-bot/src/send/mod.rs index ad87703..38c2b18 100644 --- a/crates/xmedia-bot/src/send/mod.rs +++ b/crates/xmedia-bot/src/send/mod.rs @@ -1646,6 +1646,9 @@ mod tests { .expect("the prompt record must be stored"); assert_eq!(record.url, "https://x.com/u/status/1"); assert_eq!(record.forward_message_ids, vec![10, 11]); + // The prompt replies to the post's first media message, so several + // links in one user message still get distinguishable prompts. + assert_eq!(sender.message_replies(), vec![Some(10)]); } #[tokio::test] diff --git a/crates/xmedia-bot/src/send/post_send.rs b/crates/xmedia-bot/src/send/post_send.rs index f506644..e991f79 100644 --- a/crates/xmedia-bot/src/send/post_send.rs +++ b/crates/xmedia-bot/src/send/post_send.rs @@ -301,12 +301,18 @@ pub(crate) async fn post_send_actions(ctx: &AppContext<'_>, task: &Task, message "\n({hidden} more templates not shown — /remove_template to prune.)" )); } + // Reply to this post's own first media message rather than the user's + // link message: one message carrying several links opens several + // prompts, and replying them all to that one message made each + // indistinguishable from the others. The media message is the thing + // the prompt actually edits and forwards. + let prompt_reply_to = message_ids.first().copied().unwrap_or(reply_to); let prompt = ctx .sender .send_message( ChatId(chat_id), text, - Some(MessageId(reply_to as i32)), + Some(MessageId(prompt_reply_to as i32)), Some(keyboard), ) .await;