From d8b4ab53fe8bd99ae9e6cd891ee42b2f977811a4 Mon Sep 17 00:00:00 2001 From: ishowshao <234047485@qq.com> Date: Mon, 4 May 2026 17:43:54 +0800 Subject: [PATCH] Add AI-generated content declaration option --- tests/test_client.py | 26 ++++++++++++++++++++++++++ xhs_cli/client_mixins.py | 3 +++ xhs_cli/commands/creator.py | 3 +++ 3 files changed, 32 insertions(+) diff --git a/tests/test_client.py b/tests/test_client.py index eb55af7..729d5d5 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -1,5 +1,6 @@ """Unit tests for XHS client request payloads, cookies, and endpoint selection.""" +import json from collections import OrderedDict import httpx @@ -32,6 +33,31 @@ def fake_post(self, uri, data, header_overrides=None): class TestCreatorEndpoints: + def test_create_image_note_can_mark_ai_generated_content(self, monkeypatch): + captured = {} + + def fake_post(self, uri, data, header_overrides=None): + captured["uri"] = uri + captured["data"] = data + return {"id": "note-123"} + + monkeypatch.setattr(XhsClient, "_main_api_post", fake_post) + + client = XhsClient({"a1": "cookie"}) + try: + client.create_image_note( + title="title", + desc="body", + image_file_ids=["file-1"], + ai_generated=True, + ) + finally: + client.close() + + business_binds = json.loads(captured["data"]["common"]["business_binds"]) + assert captured["uri"] == "/web_api/sns/v2/note" + assert business_binds["userDeclarationBind"] == {"origin": 2} + def test_creator_note_list_uses_v2_endpoint(self, monkeypatch): captured = {} diff --git a/xhs_cli/client_mixins.py b/xhs_cli/client_mixins.py index b4dada7..f45c9b7 100644 --- a/xhs_cli/client_mixins.py +++ b/xhs_cli/client_mixins.py @@ -577,6 +577,7 @@ def create_image_note( image_file_ids: list[str], topics: list[dict[str, str]] | None = None, is_private: bool = False, + ai_generated: bool = False, ) -> Any: images = [{"file_id": fid, "metadata": {"source": -1}} for fid in image_file_ids] business_binds = { @@ -586,6 +587,8 @@ def create_image_note( "notePostTiming": {"postTime": None}, "noteCollectionBind": {"id": ""}, } + if ai_generated: + business_binds["userDeclarationBind"] = {"origin": 2} data = { "common": { "type": "normal", diff --git a/xhs_cli/commands/creator.py b/xhs_cli/commands/creator.py index 63237f5..450f3cc 100644 --- a/xhs_cli/commands/creator.py +++ b/xhs_cli/commands/creator.py @@ -31,6 +31,7 @@ def extract_hashtags(body: str) -> list[str]: @click.option("--images", required=True, multiple=True, help="Image file path(s)") @click.option("--topic", "topics_flag", multiple=True, help="Topic(s)/hashtag(s) to search and attach") @click.option("--private", "is_private", is_flag=True, help="Publish as private note") +@click.option("--ai-generated", is_flag=True, help="Mark the note as containing AI-generated content") @structured_output_options @click.pass_context def post( @@ -40,6 +41,7 @@ def post( images: tuple[str, ...], topics_flag: tuple[str, ...], is_private: bool, + ai_generated: bool, as_json: bool, as_yaml: bool, ): @@ -73,6 +75,7 @@ def _publish(client): image_file_ids=file_ids, topics=resolved_topics, is_private=is_private, + ai_generated=ai_generated, ) handle_command(