Angiin

feat: 屏蔽权限弹窗 + 保存草稿命令

- cdp.py: new_page 中通过 Browser.setPermission 自动拒绝位置、通知、摄像头等权限弹窗
- publish.py: 新增 save_as_draft() 离开发布页触发自动保存
- cli.py: 新增 save-draft 子命令,用户取消发布时保存草稿
@@ -420,6 +420,19 @@ def cmd_click_publish(args: argparse.Namespace) -> None: @@ -420,6 +420,19 @@ def cmd_click_publish(args: argparse.Namespace) -> None:
420 browser.close() 420 browser.close()
421 421
422 422
  423 +def cmd_save_draft(args: argparse.Namespace) -> None:
  424 + """保存为草稿(取消发布时调用)。"""
  425 + from xhs.publish import save_as_draft
  426 +
  427 + browser, page = _connect_existing(args)
  428 + try:
  429 + save_as_draft(page)
  430 + _output({"success": True, "status": "内容已保存到草稿箱"})
  431 + finally:
  432 + browser.close_page(page)
  433 + browser.close()
  434 +
  435 +
423 def cmd_long_article(args: argparse.Namespace) -> None: 436 def cmd_long_article(args: argparse.Namespace) -> None:
424 """长文模式:填写内容 + 一键排版,返回模板列表。""" 437 """长文模式:填写内容 + 一键排版,返回模板列表。"""
425 from xhs.publish_long_article import publish_long_article 438 from xhs.publish_long_article import publish_long_article
@@ -677,6 +690,10 @@ def build_parser() -> argparse.ArgumentParser: @@ -677,6 +690,10 @@ def build_parser() -> argparse.ArgumentParser:
677 sub.add_argument("--content-file", required=True, help="描述内容文件路径") 690 sub.add_argument("--content-file", required=True, help="描述内容文件路径")
678 sub.set_defaults(func=cmd_next_step) 691 sub.set_defaults(func=cmd_next_step)
679 692
  693 + # save-draft(保存草稿)
  694 + sub = subparsers.add_parser("save-draft", help="保存为草稿(取消发布时使用)")
  695 + sub.set_defaults(func=cmd_save_draft)
  696 +
680 return parser 697 return parser
681 698
682 699
@@ -572,6 +572,16 @@ class Browser: @@ -572,6 +572,16 @@ class Browser:
572 }, 572 },
573 ) 573 )
574 574
  575 + # 拒绝权限弹窗(位置、通知等)
  576 + import contextlib
  577 +
  578 + for perm in ("geolocation", "notifications", "midi", "camera", "microphone"):
  579 + with contextlib.suppress(CDPError):
  580 + self._cdp.send(
  581 + "Browser.setPermission",
  582 + {"permission": {"name": perm}, "setting": "denied"},
  583 + )
  584 +
575 # 启用必要的 domain 585 # 启用必要的 domain
576 page._send_session("Page.enable") 586 page._send_session("Page.enable")
577 page._send_session("DOM.enable") 587 page._send_session("DOM.enable")
@@ -121,6 +121,30 @@ def click_publish_button(page: Page) -> None: @@ -121,6 +121,30 @@ def click_publish_button(page: Page) -> None:
121 logger.info("发布完成") 121 logger.info("发布完成")
122 122
123 123
  124 +def save_as_draft(page: Page) -> None:
  125 + """点击「暂存离开」按钮保存草稿。"""
  126 + clicked = page.evaluate(
  127 + """
  128 + (() => {
  129 + const buttons = document.querySelectorAll('button.custom-button');
  130 + for (const btn of buttons) {
  131 + if (btn.textContent.trim() === '暂存离开') {
  132 + btn.click();
  133 + return true;
  134 + }
  135 + }
  136 + return false;
  137 + })()
  138 + """
  139 + )
  140 + if clicked:
  141 + time.sleep(2)
  142 + logger.info("已点击「暂存离开」,内容已保存到草稿箱")
  143 + else:
  144 + logger.warning("未找到「暂存离开」按钮")
  145 + raise PublishError("未找到「暂存离开」按钮")
  146 +
  147 +
124 # ========== 页面导航 ========== 148 # ========== 页面导航 ==========
125 149
126 150