feat: 屏蔽权限弹窗 + 保存草稿命令
- cdp.py: new_page 中通过 Browser.setPermission 自动拒绝位置、通知、摄像头等权限弹窗 - publish.py: 新增 save_as_draft() 离开发布页触发自动保存 - cli.py: 新增 save-draft 子命令,用户取消发布时保存草稿
Showing
3 changed files
with
51 additions
and
0 deletions
| @@ -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 |
-
Please register or login to post a comment