Metadata-Version: 2.4
Name: douyin-hot-reply
Version: 0.3.0
Summary: Unified token-free Playwright CLI for previewing and replying to Douyin comments
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: playwright<2,>=1.48

# douyin-hot-reply

跨平台、token-free 的 Python Playwright 包。0.3.0 只提供一个命令、两种模式：

```text
douyin-hot-reply --preview VIDEO     # 只读 Preview
douyin-hot-reply VIDEO REPLY --yes  # 真实 Reply
```

- **Preview**：读取页面默认推荐序第一条非置顶评论，保存截图并输出 LLM-ready JSON。
- **Reply**：消费既定回复或 LLM JSON plan，重新匹配目标评论后发送，保存截图和结构化 log。

> token-free 表示本包不导入、不调用任何 LLM、HTTP SDK 或 token API。把 Preview JSON 交给包外大模型时，大模型本身仍会消耗 token。

## 安装后立即使用（无需 source）

macOS / Linux：

```bash
python3 -m pip install --user douyin-hot-reply==0.3.0
python3 -m playwright install chromium
python3 -m douyin_hot_reply -h
```

Windows：

```powershell
py -m pip install --user douyin-hot-reply==0.3.0
py -m playwright install chromium
py -m douyin_hot_reply -h
```

`python3 -m douyin_hot_reply` 与 `douyin-hot-reply` 完全等价，而且不依赖 console-script 目录是否已加入 `PATH`。纯 PyPI wheel 无法修改父 shell 的 `PATH`；如果终端找不到短命令，直接使用模块形式即可，无需 `source` 或重开终端：

```bash
python3 -m douyin_hot_reply --preview '视频链接' --profile account
```

如果用户脚本目录已在 `PATH`，可以使用更短的统一命令：

```bash
douyin-hot-reply -h
```

macOS 也可立即用绝对路径调用，无需修改 `PATH`：

```bash
"$(python3 -m site --user-base)/bin/douyin-hot-reply" -h
```

本地 wheel：

```bash
python3 -m pip install dist/douyin_hot_reply-0.3.0-py3-none-any.whl
```

## 完整命令格式

```text
douyin-hot-reply [--preview] [VIDEO] [REPLY]
                 [--plan FILE]
                 [--target-text TEXT]
                 [--profile NAME]
                 [--headless]
                 [--channel chrome]
                 [--login-timeout SECONDS]
                 [--yes]
                 [--output FILE]
```

| 参数 | 用途 |
|---|---|
| `--preview` | 只读取，不填写、不发送；不需要 `--yes` |
| `VIDEO` | 抖音视频 ID 或完整链接 |
| `REPLY` | 已由用户或外部 LLM 决定的回复文本 |
| `--plan FILE` | 读取结构化 LLM Reply/Skip JSON |
| `--target-text TEXT` | 必须精确匹配的原评论；找不到就停止 |
| `--profile NAME` | 切换独立账号 profile，默认 `default` |
| `--headless` | 使用已登录 profile 后台运行 |
| `--channel chrome` | 首次将该 profile 绑定到 Google Chrome；默认 Playwright Chromium |
| `--login-timeout N` | 有头首次登录等待秒数，默认 600 |
| `--yes` | 真实 Reply 的强制确认；Preview 禁止使用 |
| `--output FILE` | 额外写入指定 JSON 文件 |
| `--version` | 显示版本 |

## 1. Preview：读取并交给外部 LLM

首次用有头浏览器扫码：

```bash
douyin-hot-reply --preview '视频链接' \
  --profile account \
  --channel chrome \
  --output preview.json
```

同一 profile 登录后可无头运行；浏览器绑定会自动复用，不必再次传 `--channel`：

```bash
douyin-hot-reply --preview '视频链接' \
  --profile account \
  --headless \
  --output preview.json
```

无需 `PATH` 的等价写法：

```bash
python3 -m douyin_hot_reply --preview '视频链接' \
  --profile account \
  --headless \
  --output preview.json
```

Preview 返回：

- `status=ok`、`token_free=true`；
- 实际 `browser.profile` / `browser.channel`；
- 视频 URL / ID；
- 第一条非置顶推荐评论的作者、正文、时间、点赞数和可见原文；
- Reply 防错匹配使用的 `target_text`；
- 截图路径；
- `llm_handoff.input` 和严格 `response_schema`。

推荐评论定义为 `first-recommended-non-pinned`：页面保持默认推荐排序时，第一条非置顶评论；不是按点赞数重新排序。

## 2. 外部 LLM 输出格式

把 Preview JSON 中的 `llm_handoff` 交给任意外部或本地 LLM。本包自身不会调用它。

回复计划：

```json
{
  "decision": "reply",
  "video_url": "https://www.douyin.com/video/123",
  "target_text": "需要精确匹配的原评论",
  "reply_text": "结合上下文生成的回复",
  "reason": "简短理由"
}
```

跳过计划：

```json
{
  "decision": "skip",
  "video_url": "",
  "target_text": "",
  "reply_text": "",
  "reason": "不适合回复"
}
```

## 3. Reply：消费 plan 并发送

```bash
douyin-hot-reply \
  --plan reply-plan.json \
  --profile account \
  --headless \
  --yes \
  --output reply-result.json
```

`decision=skip` 会直接返回 `status=skipped`，不会启动浏览器，也不会发送。

## 4. Reply：直接传回复文本

指定目标评论，推荐用于防止排序变化后回复错人：

```bash
douyin-hot-reply '视频链接' '回复文本' \
  --target-text '必须匹配的原评论' \
  --profile account \
  --headless \
  --yes \
  --output reply-result.json
```

不传 `--target-text` 时，回复第一条非置顶推荐评论：

```bash
douyin-hot-reply '视频链接' '回复文本' \
  --profile account \
  --headless \
  --yes
```

## 多 Chrome profile 切换

每个名称对应独立 Cookie 和登录账号。首次分别扫码，后续直接切换 `--profile`：

```bash
# 首次绑定并登录
douyin-hot-reply --preview '视频链接' --profile account-a --channel chrome
douyin-hot-reply --preview '视频链接' --profile account-b --channel chrome

# 后续无头切换，不再传 --channel
douyin-hot-reply --preview '视频链接' --profile account-a --headless
douyin-hot-reply --preview '视频链接' --profile account-b --headless
```

这些是本包管理的专用 Chrome profile，不会直接修改个人 Chrome 的 `Default` / `Profile 1`。同一 profile 禁止混用 Chrome 与 Playwright Chromium，以防 Cookie 损坏；需要换浏览器时请换 profile 名称。

## 文件位置

macOS / Linux 默认位于：

```text
~/.douyin-hot-reply/profiles/<profile>/
~/.douyin-hot-reply/artifacts/*-preview.png
~/.douyin-hot-reply/artifacts/*-reply.png
~/.douyin-hot-reply/logs/*-preview.json
~/.douyin-hot-reply/logs/*-reply.json
```

Windows 使用对应用户主目录。

## 安全约束

- Preview 不填写编辑器、不发送；
- Reply 必须显式传入 `--yes`；
- 指定 `target_text` 时，找不到原评论就不发送；
- 无头模式遇到登录、短信或安全验证会停止；
- 不读取短信、不绕过验证码或风控；
- 发送按钮一旦点击，验证失败也不会自动重试；
- `decision=skip` 不启动浏览器。

## Python API

```python
from douyin_hot_reply import preview_video, reply_to_comment

preview = preview_video("视频链接", profile="account", headless=True)
result = reply_to_comment(
    "视频链接",
    "外部 LLM 已决定的回复",
    target_text=preview["comment"]["target_text"],
    profile="account",
    headless=True,
)
```

公开 API 仍只有 `preview_video` 和 `reply_to_comment`；统一的是 CLI 命令，不是两个功能本身。

## 从 0.2 升级

```bash
python3 -m pip install --upgrade douyin-hot-reply==0.3.0
```

0.3.0 删除独立 Preview console script，统一改为：

```bash
douyin-hot-reply --preview VIDEO
```

## 测试

```bash
python3 -m pytest
```

测试覆盖统一 CLI 的 Preview/Reply/Skip/错误参数、profile 绑定、目标匹配保护、结构化 JSON、token-free 源码审计和仅 Playwright 运行时依赖。
