@11agents/cli 0.1.28 → 0.1.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -319,7 +319,13 @@ class XiaohongshuAdbPublisher:
|
|
|
319
319
|
)
|
|
320
320
|
status = "published"
|
|
321
321
|
if copy_link_after_publish:
|
|
322
|
-
link_result = self.copy_current_note_link(
|
|
322
|
+
link_result = self.copy_current_note_link(
|
|
323
|
+
device,
|
|
324
|
+
prepare=False,
|
|
325
|
+
run_dir=run_dir / "link",
|
|
326
|
+
expected_title=title,
|
|
327
|
+
expected_caption=composed_caption,
|
|
328
|
+
)
|
|
323
329
|
|
|
324
330
|
ended_epoch = int(time.time())
|
|
325
331
|
if status == "published":
|
|
@@ -757,6 +763,7 @@ class XiaohongshuAdbPublisher:
|
|
|
757
763
|
expected_caption: str,
|
|
758
764
|
) -> Path:
|
|
759
765
|
self._wait_for_publish_exit_after_confirm(device, run_dir)
|
|
766
|
+
self._restart_xiaohongshu_for_link_recovery(device, run_dir)
|
|
760
767
|
return self._open_latest_own_note_for_link(
|
|
761
768
|
device,
|
|
762
769
|
run_dir,
|
|
@@ -797,7 +804,13 @@ class XiaohongshuAdbPublisher:
|
|
|
797
804
|
|
|
798
805
|
self._open_me_tab(device, run_dir)
|
|
799
806
|
self._dismiss_link_recovery_prompts(device, run_dir)
|
|
800
|
-
self._tap_latest_profile_note(
|
|
807
|
+
self._tap_latest_profile_note(
|
|
808
|
+
device,
|
|
809
|
+
run_dir,
|
|
810
|
+
expected_title=expected_title,
|
|
811
|
+
require_expected_match=bool(expected_title or expected_caption),
|
|
812
|
+
allow_first_card_fallback=bool(expected_title or expected_caption),
|
|
813
|
+
)
|
|
801
814
|
self._wait_for_detail_screen(device, run_dir)
|
|
802
815
|
return self._verify_current_note_detail(
|
|
803
816
|
device,
|
|
@@ -807,6 +820,13 @@ class XiaohongshuAdbPublisher:
|
|
|
807
820
|
prefix="published-note-detail",
|
|
808
821
|
)
|
|
809
822
|
|
|
823
|
+
def _restart_xiaohongshu_for_link_recovery(self, device: Device, run_dir: Path) -> None:
|
|
824
|
+
self._close_xiaohongshu(device)
|
|
825
|
+
time.sleep(self._wait("after_prompt_dismiss"))
|
|
826
|
+
self.adb.screenshot(device.adb_serial, run_dir / "after-publish-app-closed.png")
|
|
827
|
+
self._launch_xiaohongshu(device)
|
|
828
|
+
self.adb.screenshot(device.adb_serial, run_dir / "after-publish-relaunch.png")
|
|
829
|
+
|
|
810
830
|
def _dismiss_link_recovery_prompts(self, device: Device, run_dir: Path) -> None:
|
|
811
831
|
for idx in range(2):
|
|
812
832
|
try:
|
|
@@ -844,7 +864,22 @@ class XiaohongshuAdbPublisher:
|
|
|
844
864
|
self._tap_coord_or_default(device, "bottom_me", self._relative_coord(0.9, 0.93), self._wait("after_me_tab"))
|
|
845
865
|
self.adb.screenshot(device.adb_serial, run_dir / "profile.png")
|
|
846
866
|
|
|
847
|
-
def _tap_latest_profile_note(
|
|
867
|
+
def _tap_latest_profile_note(
|
|
868
|
+
self,
|
|
869
|
+
device: Device,
|
|
870
|
+
run_dir: Path,
|
|
871
|
+
*,
|
|
872
|
+
expected_title: str = "",
|
|
873
|
+
require_expected_match: bool = False,
|
|
874
|
+
allow_first_card_fallback: bool = False,
|
|
875
|
+
) -> None:
|
|
876
|
+
if require_expected_match and not expected_title:
|
|
877
|
+
not_identifiable_shot = run_dir / "expected-profile-note-not-identifiable.png"
|
|
878
|
+
self.adb.screenshot(device.adb_serial, not_identifiable_shot)
|
|
879
|
+
raise XiaohongshuAutomationError(
|
|
880
|
+
"expected Xiaohongshu profile note cannot be selected safely without a title",
|
|
881
|
+
not_identifiable_shot,
|
|
882
|
+
)
|
|
848
883
|
attempts = 8 if expected_title else 3
|
|
849
884
|
for attempt in range(attempts):
|
|
850
885
|
root = ui.dump_ui(self.adb, device, run_dir / f"profile-before-latest-note-{attempt + 1}.xml")
|
|
@@ -857,7 +892,14 @@ class XiaohongshuAdbPublisher:
|
|
|
857
892
|
self._scroll_profile_notes_down_half_screen(device)
|
|
858
893
|
self.adb.screenshot(device.adb_serial, run_dir / f"profile-after-scroll-{attempt + 1}.png")
|
|
859
894
|
time.sleep(2 if expected_title else 1)
|
|
860
|
-
if
|
|
895
|
+
if allow_first_card_fallback:
|
|
896
|
+
root = ui.dump_ui(self.adb, device, run_dir / "profile-before-first-card-fallback.xml")
|
|
897
|
+
node = self._find_latest_profile_note_node(root, expected_title="")
|
|
898
|
+
if node is not None:
|
|
899
|
+
ui.tap_node(self.adb, device, node, sleep_s=self._wait("after_latest_note_tap"))
|
|
900
|
+
self.adb.screenshot(device.adb_serial, run_dir / "first-card-fallback-opened.png")
|
|
901
|
+
return
|
|
902
|
+
if expected_title or require_expected_match:
|
|
861
903
|
not_found_shot = run_dir / "expected-profile-note-not-found.png"
|
|
862
904
|
self.adb.screenshot(device.adb_serial, not_found_shot)
|
|
863
905
|
raise XiaohongshuAutomationError(
|
|
@@ -966,7 +1008,8 @@ class XiaohongshuAdbPublisher:
|
|
|
966
1008
|
continue
|
|
967
1009
|
note_text = f"{text}\n{desc}"
|
|
968
1010
|
if expected:
|
|
969
|
-
|
|
1011
|
+
note_match = _normalize_match_text(note_text)
|
|
1012
|
+
if not _looks_like_profile_note_card(node) or not _profile_card_matches_expected_title(note_match, expected):
|
|
970
1013
|
continue
|
|
971
1014
|
return node
|
|
972
1015
|
item = (top, left, node)
|
|
@@ -1462,7 +1505,7 @@ def _note_text_matches_expected(visible_text: str, expected_title: str, expected
|
|
|
1462
1505
|
title = _normalize_match_text(expected_title)
|
|
1463
1506
|
caption_fragment = _verification_fragment(expected_caption)
|
|
1464
1507
|
|
|
1465
|
-
if title and title not in haystack:
|
|
1508
|
+
if title and title not in haystack and not _visible_text_matches_truncated_title(haystack, title):
|
|
1466
1509
|
return False, f"title not visible: {expected_title!r}"
|
|
1467
1510
|
if caption_fragment and caption_fragment not in haystack:
|
|
1468
1511
|
return False, f"caption fragment not visible: {caption_fragment!r}"
|
|
@@ -1471,6 +1514,13 @@ def _note_text_matches_expected(visible_text: str, expected_title: str, expected
|
|
|
1471
1514
|
return True, ""
|
|
1472
1515
|
|
|
1473
1516
|
|
|
1517
|
+
def _visible_text_matches_truncated_title(visible_text: str, expected_title: str) -> bool:
|
|
1518
|
+
if not visible_text or not expected_title:
|
|
1519
|
+
return False
|
|
1520
|
+
min_prefix = min(18, len(expected_title))
|
|
1521
|
+
return len(expected_title) >= min_prefix and expected_title[:min_prefix] in visible_text
|
|
1522
|
+
|
|
1523
|
+
|
|
1474
1524
|
def _looks_like_profile_note_card(node) -> bool:
|
|
1475
1525
|
text = node.attrib.get("text", "")
|
|
1476
1526
|
desc = node.attrib.get("content-desc", "")
|
|
@@ -1570,6 +1620,19 @@ def _verification_fragment(text: str) -> str:
|
|
|
1570
1620
|
return normalized[:18]
|
|
1571
1621
|
|
|
1572
1622
|
|
|
1623
|
+
def _profile_card_matches_expected_title(card_text: str, expected_title: str) -> bool:
|
|
1624
|
+
if not card_text or not expected_title:
|
|
1625
|
+
return False
|
|
1626
|
+
if expected_title in card_text:
|
|
1627
|
+
return True
|
|
1628
|
+
min_prefix = min(18, len(expected_title))
|
|
1629
|
+
if len(card_text) >= min_prefix and card_text in expected_title:
|
|
1630
|
+
return True
|
|
1631
|
+
if len(card_text) >= min_prefix and expected_title.startswith(card_text):
|
|
1632
|
+
return True
|
|
1633
|
+
return expected_title[:min_prefix] in card_text
|
|
1634
|
+
|
|
1635
|
+
|
|
1573
1636
|
def _strip_hashtags(text: str) -> str:
|
|
1574
1637
|
return re.sub(r"#[^\s#]+", "", text or "").strip()
|
|
1575
1638
|
|