From f0f3d3cf22c7723de146884fc90ac2dc09ceaf25 Mon Sep 17 00:00:00 2001 From: jakevin Date: Thu, 14 May 2026 16:08:48 +0800 Subject: [PATCH] feat(favorites): expose article url field (#50) Co-authored-by: Kyrie --- src/daemon/query.rs | 44 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/src/daemon/query.rs b/src/daemon/query.rs index 98574ab..03b764b 100644 --- a/src/daemon/query.rs +++ b/src/daemon/query.rs @@ -1444,6 +1444,15 @@ fn appmsg_url_for_message(local_type: i64, content: &str) -> Option { extract_appmsg_url(content) } +fn extract_favorite_url(content: &str) -> Option { + let url = extract_xml_text(content, "link") + .map(|s| unescape_html(strip_xml_cdata(&s)))?; + if url.is_empty() || !(url.starts_with("http://") || url.starts_with("https://")) { + return None; + } + Some(url) +} + fn strip_xml_cdata(s: &str) -> &str { s.strip_prefix("")) @@ -2275,7 +2284,7 @@ pub async fn q_favorites( }; // WeChat 部分版本的 update_time 为毫秒,10位以上判定为毫秒后转秒 let ts_secs = if ts > 9_999_999_999 { ts / 1000 } else { ts }; - json!({ + let mut item = json!({ "id": local_id, "type": type_str, "type_num": ftype, @@ -2284,7 +2293,13 @@ pub async fn q_favorites( "preview": preview, "from": fromusr, "chat": chatname, - }) + }); + if ftype == 5 { + if let Some(url) = extract_favorite_url(&content) { + item["url"] = Value::String(url); + } + } + item }) .collect(); @@ -3624,6 +3639,31 @@ mod sns_tests { assert_eq!(extract_appmsg_url(xml), None); } + #[test] + fn extract_favorite_url_reads_link_tag() { + let xml = concat!( + "", + "5", + "", + "" + ); + assert_eq!( + extract_favorite_url(xml).as_deref(), + Some("https://mp.weixin.qq.com/s?__biz=foo&mid=1") + ); + } + + #[test] + fn extract_favorite_url_ignores_non_http_values() { + let xml = concat!( + "", + "5", + "weixin://favorites/item/1", + "" + ); + assert_eq!(extract_favorite_url(xml), None); + } + fn media_object(value: &Value) -> &serde_json::Map { value.as_object().expect("media entry should be an object") }