✨ feat(auto): 新增自动点赞和自动回复功能,完善项目文档
- 新增自动点赞功能【一键点赞】:支持关键词搜索笔记并随机批量点赞,提升账号活跃度 - 新增自动回复功能【一键回复】:自动扫描用户笔记的粉丝评论,使用AI生成并发送回复 - 扩展自动化调度器【定时调度】:支持点赞和回复任务的随机定时执行,模拟真人操作间隔 - 新增项目文档【文档】:添加README、CHANGELOG、CONTRIBUTING、LICENSE等核心文档文件 - 优化.gitignore文件【配置】:完善Python项目、IDE、敏感文件、日志等忽略规则 - 新增配置文件模板【配置】:提供config.example.json作为配置参考 - 优化MCP客户端【工具】:新增评论解析方法,支持从笔记详情中提取结构化评论数据
This commit is contained in:
parent
d27ffe94f4
commit
d782bb6781
48
.gitignore
vendored
48
.gitignore
vendored
@ -1,3 +1,45 @@
|
||||
xhs_workspace
|
||||
__pycache__
|
||||
*.log
|
||||
# ========== 工作空间 ==========
|
||||
xhs_workspace/
|
||||
|
||||
# ========== Python ==========
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
*.egg-info/
|
||||
dist/
|
||||
build/
|
||||
.eggs/
|
||||
|
||||
# ========== 虚拟环境 ==========
|
||||
.venv/
|
||||
venv/
|
||||
env/
|
||||
|
||||
# ========== 敏感配置 ==========
|
||||
config.json
|
||||
cookies.json
|
||||
*.cookie
|
||||
|
||||
# ========== 日志 ==========
|
||||
*.log
|
||||
logs/
|
||||
|
||||
# ========== IDE ==========
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# ========== 系统文件 ==========
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
desktop.ini
|
||||
|
||||
# ========== 备份文件 ==========
|
||||
*_backup.py
|
||||
config copy.json
|
||||
|
||||
# ========== 临时文件 ==========
|
||||
*.tmp
|
||||
*.bak
|
||||
51
CHANGELOG.md
Normal file
51
CHANGELOG.md
Normal file
@ -0,0 +1,51 @@
|
||||
# 更新日志
|
||||
|
||||
本项目遵循 [Semantic Versioning](https://semver.org/) 语义化版本规范。
|
||||
|
||||
## [2.0.0] - 2026-02-08
|
||||
|
||||
### 🚀 新功能
|
||||
|
||||
- **自动运营模块**
|
||||
- 一键智能评论:自动搜索高赞笔记 → AI 分析内容 → 生成评论 → 发送
|
||||
- 一键自动点赞:批量搜索笔记并随机点赞,提升账号活跃度
|
||||
- 一键自动回复:扫描我的笔记评论 → AI 生成回复 → 逐条发送
|
||||
- 一键智能发布:自动生成主题 + 文案 + SD 绘图 + 发布到小红书
|
||||
- 随机定时调度:评论/点赞/回复/发布四项可任意组合,随机间隔模拟真人
|
||||
- 实时运行日志面板
|
||||
|
||||
- **评论管家**
|
||||
- 主动评论引流:浏览笔记 → AI 智能生成评论 → 一键发送
|
||||
- 回复粉丝评论:加载我的笔记评论 → AI 回复 → 发送
|
||||
|
||||
- **热点探测**
|
||||
- 关键词搜索小红书热门笔记
|
||||
- AI 趋势分析(标题套路、内容结构、推荐选题)
|
||||
- 基于热点参考生成原创文案
|
||||
|
||||
- **数据看板**
|
||||
- 账号核心指标可视化(粉丝、获赞、收藏)
|
||||
- 笔记点赞排行图表
|
||||
|
||||
- **多 LLM 提供商**
|
||||
- 支持添加/切换/删除多个 LLM 提供商
|
||||
- 兼容所有 OpenAI 接口(DeepSeek、GPT、通义千问等)
|
||||
|
||||
- **账号管理**
|
||||
- 小红书扫码登录
|
||||
- 自动获取 xsec_token
|
||||
|
||||
### 🎨 优化
|
||||
|
||||
- SD 参数针对 JuggernautXL/SDXL 优化(CFG 5.0、DPM++ 2M SDE Karras、832×1216)
|
||||
- 负面提示词增强(SDXL 专用)
|
||||
- LLM 绘图 Prompt 模板优化(photorealistic、cinematic 风格引导)
|
||||
|
||||
## [1.0.0] - 2026-01-xx
|
||||
|
||||
### 🚀 初始版本
|
||||
|
||||
- 基础内容创作流程:主题输入 → LLM 文案 → SD 绘图 → 一键发布
|
||||
- 支持图文发布到小红书
|
||||
- 本地文案导出
|
||||
- Gradio Web UI
|
||||
118
CONTRIBUTING.md
Normal file
118
CONTRIBUTING.md
Normal file
@ -0,0 +1,118 @@
|
||||
# 贡献指南
|
||||
|
||||
感谢你对本项目的关注!我们欢迎任何形式的贡献,包括但不限于 Bug 报告、功能建议、代码提交和文档改进。
|
||||
|
||||
## 开发环境搭建
|
||||
|
||||
```bash
|
||||
# 1. Fork 并克隆项目
|
||||
git clone https://github.com/your-username/xhs-autobot.git
|
||||
cd xhs-autobot
|
||||
|
||||
# 2. 创建虚拟环境
|
||||
python -m venv .venv
|
||||
# Windows
|
||||
.venv\Scripts\activate
|
||||
# macOS/Linux
|
||||
source .venv/bin/activate
|
||||
|
||||
# 3. 安装依赖
|
||||
pip install -r requirements.txt
|
||||
|
||||
# 4. 复制配置文件
|
||||
cp config.example.json config.json
|
||||
# 编辑 config.json 填写你的 API Key
|
||||
|
||||
# 5. 启动开发
|
||||
python main.py
|
||||
```
|
||||
|
||||
## 提交规范
|
||||
|
||||
本项目使用 [Conventional Commits](https://www.conventionalcommits.org/) 规范:
|
||||
|
||||
```
|
||||
<type>(<scope>): <description>
|
||||
|
||||
[可选正文]
|
||||
|
||||
[可选脚注]
|
||||
```
|
||||
|
||||
### Type 类型
|
||||
|
||||
| 类型 | 说明 |
|
||||
|------|------|
|
||||
| `feat` | 新功能 |
|
||||
| `fix` | Bug 修复 |
|
||||
| `docs` | 文档更新 |
|
||||
| `style` | 代码格式调整(不影响逻辑) |
|
||||
| `refactor` | 重构(非新功能、非修复) |
|
||||
| `perf` | 性能优化 |
|
||||
| `test` | 测试相关 |
|
||||
| `chore` | 构建/工具变更 |
|
||||
|
||||
### 示例
|
||||
|
||||
```
|
||||
feat(auto): 增加自动收藏功能
|
||||
fix(llm): 修复 JSON 模式下 400 错误
|
||||
docs: 更新 README 安装步骤
|
||||
refactor(mcp): 重构评论解析逻辑
|
||||
```
|
||||
|
||||
## Pull Request 流程
|
||||
|
||||
1. **Fork** 本仓库
|
||||
2. 从 `main` 创建特性分支:`git checkout -b feature/your-feature`
|
||||
3. 编写代码并测试
|
||||
4. 确保代码风格一致(建议使用 IDE 自动格式化)
|
||||
5. 提交更改,遵循上述提交规范
|
||||
6. 推送分支:`git push origin feature/your-feature`
|
||||
7. 在 GitHub 上发起 **Pull Request**,描述你的更改内容
|
||||
|
||||
## Bug 报告
|
||||
|
||||
提交 Issue 时请包含:
|
||||
|
||||
- **环境信息**:Python 版本、操作系统、相关服务版本
|
||||
- **复现步骤**:尽可能详细的操作步骤
|
||||
- **期望行为**:你认为应该发生什么
|
||||
- **实际行为**:实际发生了什么
|
||||
- **日志/截图**:如有错误日志或截图请附上
|
||||
|
||||
## 功能建议
|
||||
|
||||
欢迎通过 Issue 提交功能建议,请描述:
|
||||
|
||||
- **使用场景**:你在什么情况下需要这个功能
|
||||
- **期望功能**:你希望它如何工作
|
||||
- **参考实现**:是否有类似的项目/功能可参考
|
||||
|
||||
## 项目架构
|
||||
|
||||
```
|
||||
main.py # 主程序:Gradio UI + 业务逻辑 + 自动化调度
|
||||
├─ config_manager.py # 配置管理:单例模式,多 LLM 提供商
|
||||
├─ llm_service.py # LLM 封装:文案生成、热点分析、评论回复
|
||||
├─ sd_service.py # SD 封装:txt2img、img2img(JuggernautXL 优化)
|
||||
└─ mcp_client.py # MCP 客户端:小红书搜索、发布、评论、点赞
|
||||
```
|
||||
|
||||
### 核心设计原则
|
||||
|
||||
- **模块解耦** — 各服务独立封装,通过配置管理器共享状态
|
||||
- **MCP 协议** — 通过 JSON-RPC 与小红书 MCP 服务通信,不直接操作浏览器
|
||||
- **LLM 无关** — 支持所有 OpenAI 兼容 API,不绑定特定提供商
|
||||
- **UI 逻辑分离** — 业务函数与 Gradio UI 组件分开定义
|
||||
|
||||
## 代码风格
|
||||
|
||||
- Python 3.10+ 语法
|
||||
- 函数和类使用中文 docstring
|
||||
- 日志使用 `logging` 模块,不使用 `print`
|
||||
- 配置通过 `ConfigManager` 单例管理,不硬编码
|
||||
|
||||
## 感谢
|
||||
|
||||
感谢每一位贡献者!🙏
|
||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 xhs-autobot contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
263
README.md
Normal file
263
README.md
Normal file
@ -0,0 +1,263 @@
|
||||
<p align="center">
|
||||
<h1 align="center">🍒 小红书 AI 爆文生产工坊</h1>
|
||||
<p align="center">
|
||||
<strong>全自动小红书内容创作 & 运营工具</strong><br>
|
||||
灵感 → 文案 → 绘图 → 发布 → 运营,一站式全闭环
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="#功能特性">功能特性</a> •
|
||||
<a href="#快速开始">快速开始</a> •
|
||||
<a href="#使用指南">使用指南</a> •
|
||||
<a href="#配置说明">配置说明</a> •
|
||||
<a href="#常见问题">FAQ</a> •
|
||||
<a href="#贡献指南">贡献</a>
|
||||
</p>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
## ✨ 功能特性
|
||||
|
||||
### 📝 内容创作
|
||||
- **AI 文案生成** — 输入主题即可生成小红书爆款标题、正文、话题标签
|
||||
- **AI 绘图** — 集成 Stable Diffusion WebUI,自动生成配图(针对 JuggernautXL 优化)
|
||||
- **一键导出** — 文案 + 图片打包导出到本地文件夹
|
||||
|
||||
### 🔥 热点探测
|
||||
- **关键词搜索** — 搜索小红书热门笔记,支持多维度排序
|
||||
- **AI 趋势分析** — 分析热门标题套路、内容结构,给出模仿建议
|
||||
- **一键借鉴创作** — 参考热门笔记风格生成原创内容
|
||||
|
||||
### 💬 评论管家
|
||||
- **主动评论引流** — 浏览笔记 → AI 智能生成评论 → 一键发送
|
||||
- **自动回复粉丝** — 加载笔记评论 → AI 生成回复 → 发送
|
||||
|
||||
### 🤖 自动运营(无人值守)
|
||||
- **一键评论** — 自动搜索高赞笔记 + AI 生成评论 + 发送
|
||||
- **一键点赞** — 批量随机点赞,提升账号活跃度
|
||||
- **一键回复** — 自动扫描我的笔记 + AI 回复粉丝评论
|
||||
- **一键发布** — 自动生成文案 + SD 生图 + 发布到小红书
|
||||
- **随机定时** — 评论/点赞/回复/发布全自动定时执行,随机间隔模拟真人
|
||||
|
||||
### 📊 数据看板
|
||||
- **账号概览** — 粉丝数、获赞数等核心指标可视化
|
||||
- **笔记排行** — 点赞排行图表分析
|
||||
|
||||
### 🔐 账号管理
|
||||
- **扫码登录** — 小红书二维码登录,自动获取 Token
|
||||
- **多 LLM 提供商** — 支持 DeepSeek、OpenAI、通义千问等所有兼容接口
|
||||
|
||||
---
|
||||
|
||||
## 📸 截图预览
|
||||
|
||||
> 启动后在浏览器中打开 `http://127.0.0.1:7860`
|
||||
|
||||
---
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
### 环境要求
|
||||
|
||||
| 依赖 | 要求 | 说明 |
|
||||
|------|------|------|
|
||||
| **Python** | >= 3.10 | 推荐 3.11+ |
|
||||
| **xiaohongshu-mcp** | 运行中 | 小红书 MCP 服务(默认端口 18060) |
|
||||
| **Stable Diffusion WebUI** | 可选 | 本地 AI 绘图(默认端口 7860) |
|
||||
| **LLM API** | 必须 | 任意 OpenAI 兼容接口 |
|
||||
|
||||
### 安装步骤
|
||||
|
||||
```bash
|
||||
# 1. 克隆项目
|
||||
git clone https://github.com/your-username/xhs-autobot.git
|
||||
cd xhs-autobot
|
||||
|
||||
# 2. 创建虚拟环境(推荐)
|
||||
python -m venv .venv
|
||||
# Windows
|
||||
.venv\Scripts\activate
|
||||
# macOS/Linux
|
||||
source .venv/bin/activate
|
||||
|
||||
# 3. 安装依赖
|
||||
pip install -r requirements.txt
|
||||
|
||||
# 4. 复制配置文件并填写你的 API Key
|
||||
cp config.example.json config.json
|
||||
# 编辑 config.json,填写 api_key、base_url 等
|
||||
|
||||
# 5. 启动!
|
||||
python main.py
|
||||
```
|
||||
|
||||
启动后会自动打开浏览器,访问 `http://127.0.0.1:7860`。
|
||||
|
||||
### 前置服务
|
||||
|
||||
#### xiaohongshu-mcp(必须)
|
||||
|
||||
本项目通过 [xiaohongshu-mcp](https://github.com/punkpeye/xiaohongshu-mcp) 与小红书交互(搜索、发布、评论等),请先启动 MCP 服务:
|
||||
|
||||
```bash
|
||||
# 按照 xiaohongshu-mcp 文档启动,默认端口 18060
|
||||
npx xiaohongshu-mcp
|
||||
```
|
||||
|
||||
#### Stable Diffusion WebUI(可选,用于 AI 绘图)
|
||||
|
||||
推荐使用 [AUTOMATIC1111/stable-diffusion-webui](https://github.com/AUTOMATIC1111/stable-diffusion-webui),启动时开启 API:
|
||||
|
||||
```bash
|
||||
python launch.py --api
|
||||
```
|
||||
|
||||
推荐模型:[JuggernautXL](https://civitai.com/models/133005)(参数已优化适配)。
|
||||
|
||||
---
|
||||
|
||||
## 📖 使用指南
|
||||
|
||||
### 首次使用流程
|
||||
|
||||
1. **配置 LLM** — 展开「⚙️ 全局设置」,添加 LLM 提供商(API Key + Base URL),点击「连接 LLM」
|
||||
2. **连接 SD**(可选)— 填写 SD WebUI URL,点击「连接 SD」
|
||||
3. **检查 MCP** — 点击「检查 MCP」确认小红书服务正常
|
||||
4. **登录小红书** — 切换到「🔐 账号登录」Tab,扫码登录
|
||||
5. **开始创作** — 切换到「✨ 内容创作」Tab,输入主题,一键生成
|
||||
|
||||
### 自动化运营
|
||||
|
||||
切换到「🤖 自动运营」Tab:
|
||||
|
||||
- **一键操作** — 手动触发单次评论/点赞/回复/发布
|
||||
- **定时调度** — 勾选需要的功能,设置间隔时间,点击「▶️ 启动定时」
|
||||
- **查看日志** — 点击「🔄 刷新日志」查看实时执行记录
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ 配置说明
|
||||
|
||||
配置文件 `config.json` 会在运行时自动创建和保存。首次使用请从 `config.example.json` 复制:
|
||||
|
||||
```json
|
||||
{
|
||||
"api_key": "你的LLM API Key",
|
||||
"base_url": "https://api.deepseek.com/v1",
|
||||
"sd_url": "http://127.0.0.1:7860",
|
||||
"mcp_url": "http://localhost:18060/mcp",
|
||||
"model": "deepseek-chat",
|
||||
"persona": "温柔知性的时尚博主",
|
||||
"my_user_id": "你的小红书userId(24位)"
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 说明 | 必填 |
|
||||
|------|------|------|
|
||||
| `api_key` | LLM API 密钥 | ✅ |
|
||||
| `base_url` | LLM API 地址(OpenAI 兼容) | ✅ |
|
||||
| `sd_url` | Stable Diffusion WebUI 地址 | 绘图需要 |
|
||||
| `mcp_url` | xiaohongshu-mcp 服务地址 | ✅ |
|
||||
| `model` | 默认使用的 LLM 模型名 | ✅ |
|
||||
| `persona` | AI 评论回复的人设 | 可选 |
|
||||
| `my_user_id` | 你的小红书 userId(24 位十六进制) | 数据看板/自动回复需要 |
|
||||
| `llm_providers` | 多 LLM 提供商配置数组 | 通过 UI 管理 |
|
||||
|
||||
### 获取 userId
|
||||
|
||||
1. 浏览器打开你的小红书主页
|
||||
2. 网址格式为 `xiaohongshu.com/user/profile/xxxxxxxx`
|
||||
3. `profile/` 后面的就是 userId
|
||||
|
||||
---
|
||||
|
||||
## 📁 项目结构
|
||||
|
||||
```
|
||||
xhs-autobot/
|
||||
├── main.py # 主程序入口 (Gradio UI + 业务逻辑)
|
||||
├── config_manager.py # 配置管理模块 (单例、自动保存)
|
||||
├── llm_service.py # LLM 服务封装 (文案生成、热点分析、评论回复)
|
||||
├── sd_service.py # Stable Diffusion 服务封装 (txt2img、img2img)
|
||||
├── mcp_client.py # 小红书 MCP 客户端 (搜索、发布、评论、点赞)
|
||||
├── config.json # 运行时配置 (gitignore)
|
||||
├── config.example.json # 配置模板
|
||||
├── requirements.txt # Python 依赖
|
||||
├── xhs_workspace/ # 导出的文案和图片 (gitignore)
|
||||
└── autobot.log # 运行日志 (gitignore)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ❓ 常见问题
|
||||
|
||||
<details>
|
||||
<summary><b>Q: LLM API 报错 400 json_object</b></summary>
|
||||
|
||||
某些 API 在使用 `response_format: json_object` 时要求消息中包含 "json" 一词。本项目已自动处理,如仍遇到请升级到最新版本。
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Q: 评论发送成功但 App 上看不到</b></summary>
|
||||
|
||||
小红书有内容审核机制,评论可能需要 1-5 分钟显示。部分评论可能被风控(仅自己可见)。查看自动化日志中的 MCP 响应可排查。
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Q: SD WebUI 连接失败</b></summary>
|
||||
|
||||
确保启动 SD WebUI 时加了 `--api` 参数,且端口匹配。本项目默认连接 `http://127.0.0.1:7860`。
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Q: xiaohongshu-mcp 是什么?怎么启动?</b></summary>
|
||||
|
||||
这是一个开源的小红书 MCP 服务端,提供搜索、发布、评论等 API。详见 [xiaohongshu-mcp 项目](https://github.com/punkpeye/xiaohongshu-mcp)。
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Q: 支持哪些 LLM?</b></summary>
|
||||
|
||||
支持所有 OpenAI 兼容接口,包括但不限于:DeepSeek、GPT-4o、通义千问、Gemini(通过中转)、Claude(通过中转)等。
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
## 🤝 贡献指南
|
||||
|
||||
欢迎贡献代码!请查看 [CONTRIBUTING.md](CONTRIBUTING.md) 了解详情。
|
||||
|
||||
简要流程:
|
||||
1. Fork 本项目
|
||||
2. 创建特性分支 (`git checkout -b feature/amazing-feature`)
|
||||
3. 提交更改 (`git commit -m 'feat: add amazing feature'`)
|
||||
4. 推送到分支 (`git push origin feature/amazing-feature`)
|
||||
5. 发起 Pull Request
|
||||
|
||||
---
|
||||
|
||||
## 📋 更新日志
|
||||
|
||||
详见 [CHANGELOG.md](CHANGELOG.md)。
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 免责声明
|
||||
|
||||
- 本项目仅供学习和研究目的,请遵守小红书平台的使用规范和服务条款
|
||||
- 过度使用自动化功能可能导致账号被限制,请合理设置操作间隔
|
||||
- 用户需为自己发布的内容和使用行为承担全部责任
|
||||
- 本项目不保存、不传输任何用户的账号密码信息
|
||||
|
||||
---
|
||||
|
||||
## 📄 许可证
|
||||
|
||||
本项目使用 [MIT License](LICENSE) 开源。
|
||||
|
||||
---
|
||||
|
||||
## 🌟 Star History
|
||||
|
||||
如果这个项目对你有帮助,请点亮 ⭐ Star!
|
||||
|
||||
20
config.example.json
Normal file
20
config.example.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"api_key": "sk-your-api-key-here",
|
||||
"base_url": "https://api.deepseek.com/v1",
|
||||
"sd_url": "http://127.0.0.1:7860",
|
||||
"mcp_url": "http://localhost:18060/mcp",
|
||||
"model": "deepseek-chat",
|
||||
"persona": "温柔知性的时尚博主",
|
||||
"auto_reply_enabled": false,
|
||||
"schedule_enabled": false,
|
||||
"my_user_id": "",
|
||||
"active_llm": "默认",
|
||||
"llm_providers": [
|
||||
{
|
||||
"name": "默认",
|
||||
"api_key": "sk-your-api-key-here",
|
||||
"base_url": "https://api.deepseek.com/v1"
|
||||
}
|
||||
],
|
||||
"xsec_token": ""
|
||||
}
|
||||
352
main.py
352
main.py
@ -1007,12 +1007,233 @@ def auto_comment_once(keywords_str, mcp_url, model, persona_text):
|
||||
return f"❌ 评论失败: {e}"
|
||||
|
||||
|
||||
def _auto_like_with_log(keywords_str, like_count, mcp_url):
|
||||
"""一键点赞 + 同步刷新日志"""
|
||||
msg = auto_like_once(keywords_str, like_count, mcp_url)
|
||||
return msg, get_auto_log()
|
||||
|
||||
|
||||
def auto_like_once(keywords_str, like_count, mcp_url):
|
||||
"""一键点赞:搜索/推荐笔记 → 随机选择 → 批量点赞"""
|
||||
try:
|
||||
keywords = [k.strip() for k in keywords_str.split(",") if k.strip()] if keywords_str else DEFAULT_COMMENT_KEYWORDS
|
||||
keyword = random.choice(keywords)
|
||||
like_count = int(like_count) if like_count else 5
|
||||
_auto_log_append(f"👍 点赞关键词: {keyword} | 目标: {like_count} 个")
|
||||
|
||||
client = get_mcp_client(mcp_url)
|
||||
|
||||
# 搜索笔记
|
||||
entries = client.search_feeds_parsed(keyword, sort_by="综合")
|
||||
if not entries:
|
||||
_auto_log_append("⚠️ 搜索无结果,尝试推荐列表")
|
||||
entries = client.list_feeds_parsed()
|
||||
if not entries:
|
||||
return "❌ 未找到任何笔记"
|
||||
|
||||
# 过滤自己的笔记
|
||||
my_uid = cfg.get("my_user_id", "")
|
||||
if my_uid:
|
||||
filtered = [e for e in entries if e.get("user_id") != my_uid]
|
||||
if filtered:
|
||||
entries = filtered
|
||||
|
||||
# 随机打乱,取前 N 个
|
||||
random.shuffle(entries)
|
||||
targets = entries[:min(like_count, len(entries))]
|
||||
|
||||
liked = 0
|
||||
for target in targets:
|
||||
feed_id = target.get("feed_id", "")
|
||||
xsec_token = target.get("xsec_token", "")
|
||||
title = target.get("title", "未知")[:25]
|
||||
|
||||
if not feed_id or not xsec_token:
|
||||
continue
|
||||
|
||||
# 模拟浏览延迟
|
||||
time.sleep(random.uniform(2, 6))
|
||||
|
||||
result = client.like_feed(feed_id, xsec_token)
|
||||
if "error" in result:
|
||||
_auto_log_append(f" ❌ 点赞失败「{title}」: {result['error']}")
|
||||
else:
|
||||
liked += 1
|
||||
_auto_log_append(f" ❤️ 已点赞「{title}」@{target.get('author', '未知')}")
|
||||
|
||||
_auto_log_append(f"👍 点赞完成: 成功 {liked}/{len(targets)}")
|
||||
return f"✅ 点赞完成!成功 {liked}/{len(targets)} 个"
|
||||
|
||||
except Exception as e:
|
||||
_auto_log_append(f"❌ 一键点赞异常: {e}")
|
||||
return f"❌ 点赞失败: {e}"
|
||||
|
||||
|
||||
def _auto_publish_with_log(topics_str, mcp_url, sd_url_val, sd_model_name, model):
|
||||
"""一键发布 + 同步刷新日志"""
|
||||
msg = auto_publish_once(topics_str, mcp_url, sd_url_val, sd_model_name, model)
|
||||
return msg, get_auto_log()
|
||||
|
||||
|
||||
def _auto_reply_with_log(max_replies, mcp_url, model, persona_text):
|
||||
"""一键回复 + 同步刷新日志"""
|
||||
msg = auto_reply_once(max_replies, mcp_url, model, persona_text)
|
||||
return msg, get_auto_log()
|
||||
|
||||
|
||||
def auto_reply_once(max_replies, mcp_url, model, persona_text):
|
||||
"""一键回复:获取我的笔记 → 加载评论 → AI 生成回复 → 发送"""
|
||||
try:
|
||||
my_uid = cfg.get("my_user_id", "")
|
||||
xsec = cfg.get("xsec_token", "")
|
||||
if not my_uid:
|
||||
return "❌ 未配置用户 ID,请到「账号登录」页填写"
|
||||
if not xsec:
|
||||
return "❌ 未获取 xsec_token,请先登录"
|
||||
|
||||
api_key, base_url, _ = _get_llm_config()
|
||||
if not api_key:
|
||||
return "❌ LLM 未配置"
|
||||
|
||||
max_replies = int(max_replies) if max_replies else 3
|
||||
client = get_mcp_client(mcp_url)
|
||||
_auto_log_append("💌 开始自动回复评论...")
|
||||
|
||||
# Step 1: 获取我的笔记列表
|
||||
result = client.get_user_profile(my_uid, xsec)
|
||||
if "error" in result:
|
||||
_auto_log_append(f"❌ 获取我的笔记失败: {result['error']}")
|
||||
return f"❌ 获取我的笔记失败: {result['error']}"
|
||||
|
||||
# 解析笔记列表
|
||||
raw = result.get("raw", {})
|
||||
text = result.get("text", "")
|
||||
data = None
|
||||
if raw and isinstance(raw, dict):
|
||||
for item in raw.get("content", []):
|
||||
if item.get("type") == "text":
|
||||
try:
|
||||
data = json.loads(item["text"])
|
||||
except (json.JSONDecodeError, KeyError):
|
||||
pass
|
||||
if not data:
|
||||
try:
|
||||
data = json.loads(text)
|
||||
except (json.JSONDecodeError, TypeError):
|
||||
pass
|
||||
|
||||
feeds = (data or {}).get("feeds") or []
|
||||
if not feeds:
|
||||
_auto_log_append("⚠️ 未找到任何笔记")
|
||||
return "⚠️ 未找到你的笔记"
|
||||
|
||||
# 构建笔记条目
|
||||
my_entries = []
|
||||
for f in feeds:
|
||||
nc = f.get("noteCard") or {}
|
||||
my_entries.append({
|
||||
"feed_id": f.get("id", ""),
|
||||
"xsec_token": f.get("xsecToken", ""),
|
||||
"title": nc.get("displayTitle", "未知标题"),
|
||||
})
|
||||
|
||||
_auto_log_append(f"📝 找到 {len(my_entries)} 篇笔记,开始扫描评论...")
|
||||
|
||||
# Step 2: 遍历笔记,找到未回复的评论
|
||||
total_replied = 0
|
||||
svc = LLMService(api_key, base_url, model)
|
||||
|
||||
for entry in my_entries:
|
||||
if total_replied >= max_replies:
|
||||
break
|
||||
|
||||
feed_id = entry["feed_id"]
|
||||
xsec_token = entry["xsec_token"]
|
||||
title = entry["title"]
|
||||
|
||||
if not feed_id or not xsec_token:
|
||||
continue
|
||||
|
||||
time.sleep(random.uniform(1, 3))
|
||||
|
||||
# 加载笔记详情(含评论)
|
||||
detail = client.get_feed_detail(feed_id, xsec_token, load_all_comments=True)
|
||||
if "error" in detail:
|
||||
_auto_log_append(f"⚠️ 加载「{title[:15]}」评论失败,跳过")
|
||||
continue
|
||||
|
||||
full_text = detail.get("text", "")
|
||||
|
||||
# 解析评论
|
||||
comments = client._parse_comments(full_text)
|
||||
if not comments:
|
||||
continue
|
||||
|
||||
# 过滤掉自己的评论,只回复他人
|
||||
other_comments = [
|
||||
c for c in comments
|
||||
if c.get("user_id") and c["user_id"] != my_uid and c.get("content")
|
||||
]
|
||||
|
||||
if not other_comments:
|
||||
continue
|
||||
|
||||
_auto_log_append(f"📖「{title[:20]}」有 {len(other_comments)} 条他人评论")
|
||||
|
||||
for comment in other_comments:
|
||||
if total_replied >= max_replies:
|
||||
break
|
||||
|
||||
comment_id = comment.get("comment_id", "")
|
||||
comment_uid = comment.get("user_id", "")
|
||||
comment_text = comment.get("content", "")
|
||||
nickname = comment.get("nickname", "网友")
|
||||
|
||||
if not comment_text.strip():
|
||||
continue
|
||||
|
||||
_auto_log_append(f" 💬 @{nickname}: {comment_text[:40]}...")
|
||||
|
||||
# AI 生成回复
|
||||
try:
|
||||
reply = svc.generate_reply(persona_text, title, comment_text)
|
||||
except Exception as e:
|
||||
_auto_log_append(f" ❌ AI 回复生成失败: {e}")
|
||||
continue
|
||||
|
||||
_auto_log_append(f" 🤖 回复: {reply[:50]}...")
|
||||
|
||||
# 发送回复
|
||||
time.sleep(random.uniform(2, 6))
|
||||
|
||||
if comment_id and comment_uid:
|
||||
# 使用 reply_comment 精确回复
|
||||
resp = client.reply_comment(
|
||||
feed_id, xsec_token, comment_id, comment_uid, reply
|
||||
)
|
||||
else:
|
||||
# 没有 comment_id 就用 post_comment 发到笔记下
|
||||
resp = client.post_comment(feed_id, xsec_token, f"@{nickname} {reply}")
|
||||
|
||||
resp_text = resp.get("text", "")
|
||||
if "error" in resp:
|
||||
_auto_log_append(f" ❌ 回复发送失败: {resp['error']}")
|
||||
else:
|
||||
_auto_log_append(f" ✅ 已回复 @{nickname}")
|
||||
total_replied += 1
|
||||
|
||||
if total_replied == 0:
|
||||
_auto_log_append("ℹ️ 没有找到需要回复的新评论")
|
||||
return "ℹ️ 没有找到需要回复的新评论\n\n💡 可能所有评论都已回复过"
|
||||
else:
|
||||
_auto_log_append(f"✅ 自动回复完成,共回复 {total_replied} 条评论")
|
||||
return f"✅ 自动回复完成!共回复 {total_replied} 条评论\n\n💡 小红书审核可能有延迟,请稍后查看"
|
||||
|
||||
except Exception as e:
|
||||
_auto_log_append(f"❌ 自动回复异常: {e}")
|
||||
return f"❌ 自动回复失败: {e}"
|
||||
|
||||
|
||||
def auto_publish_once(topics_str, mcp_url, sd_url_val, sd_model_name, model):
|
||||
"""一键发布:自动生成文案 → 生成图片 → 发布到小红书"""
|
||||
try:
|
||||
@ -1078,8 +1299,10 @@ def auto_publish_once(topics_str, mcp_url, sd_url_val, sd_model_name, model):
|
||||
return f"❌ 发布失败: {e}"
|
||||
|
||||
|
||||
def _scheduler_loop(comment_enabled, publish_enabled,
|
||||
def _scheduler_loop(comment_enabled, publish_enabled, reply_enabled, like_enabled,
|
||||
comment_min, comment_max, publish_min, publish_max,
|
||||
reply_min, reply_max, max_replies_per_run,
|
||||
like_min, like_max, like_count_per_run,
|
||||
keywords, topics, mcp_url, sd_url_val, sd_model_name,
|
||||
model, persona_text):
|
||||
"""后台定时调度循环"""
|
||||
@ -1088,6 +1311,8 @@ def _scheduler_loop(comment_enabled, publish_enabled,
|
||||
# 首次执行的随机延迟
|
||||
next_comment = time.time() + random.randint(10, 60)
|
||||
next_publish = time.time() + random.randint(30, 120)
|
||||
next_reply = time.time() + random.randint(15, 90)
|
||||
next_like = time.time() + random.randint(5, 40)
|
||||
|
||||
while _auto_running.is_set():
|
||||
now = time.time()
|
||||
@ -1104,6 +1329,18 @@ def _scheduler_loop(comment_enabled, publish_enabled,
|
||||
next_comment = time.time() + interval
|
||||
_auto_log_append(f"⏰ 下次评论: {interval // 60} 分钟后")
|
||||
|
||||
# 自动点赞
|
||||
if like_enabled and now >= next_like:
|
||||
try:
|
||||
_auto_log_append("--- 🔄 执行自动点赞 ---")
|
||||
msg = auto_like_once(keywords, like_count_per_run, mcp_url)
|
||||
_auto_log_append(msg)
|
||||
except Exception as e:
|
||||
_auto_log_append(f"❌ 自动点赞异常: {e}")
|
||||
interval = random.randint(int(like_min) * 60, int(like_max) * 60)
|
||||
next_like = time.time() + interval
|
||||
_auto_log_append(f"⏰ 下次点赞: {interval // 60} 分钟后")
|
||||
|
||||
# 自动发布
|
||||
if publish_enabled and now >= next_publish:
|
||||
try:
|
||||
@ -1116,6 +1353,18 @@ def _scheduler_loop(comment_enabled, publish_enabled,
|
||||
next_publish = time.time() + interval
|
||||
_auto_log_append(f"⏰ 下次发布: {interval // 60} 分钟后")
|
||||
|
||||
# 自动回复评论
|
||||
if reply_enabled and now >= next_reply:
|
||||
try:
|
||||
_auto_log_append("--- 🔄 执行自动回复评论 ---")
|
||||
msg = auto_reply_once(max_replies_per_run, mcp_url, model, persona_text)
|
||||
_auto_log_append(msg)
|
||||
except Exception as e:
|
||||
_auto_log_append(f"❌ 自动回复异常: {e}")
|
||||
interval = random.randint(int(reply_min) * 60, int(reply_max) * 60)
|
||||
next_reply = time.time() + interval
|
||||
_auto_log_append(f"⏰ 下次回复: {interval // 60} 分钟后")
|
||||
|
||||
# 每5秒检查一次停止信号
|
||||
for _ in range(5):
|
||||
if not _auto_running.is_set():
|
||||
@ -1125,7 +1374,10 @@ def _scheduler_loop(comment_enabled, publish_enabled,
|
||||
_auto_log_append("🛑 自动化调度器已停止")
|
||||
|
||||
|
||||
def start_scheduler(comment_on, publish_on, c_min, c_max, p_min, p_max,
|
||||
def start_scheduler(comment_on, publish_on, reply_on, like_on,
|
||||
c_min, c_max, p_min, p_max, r_min, r_max,
|
||||
max_replies_per_run,
|
||||
l_min, l_max, like_count_per_run,
|
||||
keywords, topics, mcp_url, sd_url_val, sd_model_name,
|
||||
model, persona_text):
|
||||
"""启动定时自动化"""
|
||||
@ -1133,18 +1385,22 @@ def start_scheduler(comment_on, publish_on, c_min, c_max, p_min, p_max,
|
||||
if _auto_running.is_set():
|
||||
return "⚠️ 调度器已在运行中,请先停止"
|
||||
|
||||
if not comment_on and not publish_on:
|
||||
return "❌ 请至少启用一项自动化功能(评论或发布)"
|
||||
if not comment_on and not publish_on and not reply_on and not like_on:
|
||||
return "❌ 请至少启用一项自动化功能"
|
||||
|
||||
api_key, _, _ = _get_llm_config()
|
||||
if not api_key:
|
||||
return "❌ LLM 未配置,请先在全局设置中配置提供商"
|
||||
# 评论/回复需要 LLM,点赞不需要
|
||||
if (comment_on or reply_on):
|
||||
api_key, _, _ = _get_llm_config()
|
||||
if not api_key:
|
||||
return "❌ LLM 未配置,请先在全局设置中配置提供商"
|
||||
|
||||
_auto_running.set()
|
||||
_auto_thread = threading.Thread(
|
||||
target=_scheduler_loop,
|
||||
args=(comment_on, publish_on,
|
||||
c_min, c_max, p_min, p_max,
|
||||
args=(comment_on, publish_on, reply_on, like_on,
|
||||
c_min, c_max, p_min, p_max, r_min, r_max,
|
||||
max_replies_per_run,
|
||||
l_min, l_max, like_count_per_run,
|
||||
keywords, topics, mcp_url, sd_url_val, sd_model_name,
|
||||
model, persona_text),
|
||||
daemon=True,
|
||||
@ -1154,8 +1410,12 @@ def start_scheduler(comment_on, publish_on, c_min, c_max, p_min, p_max,
|
||||
parts = []
|
||||
if comment_on:
|
||||
parts.append(f"评论 (每 {int(c_min)}-{int(c_max)} 分钟)")
|
||||
if like_on:
|
||||
parts.append(f"点赞 (每 {int(l_min)}-{int(l_max)} 分钟, {int(like_count_per_run)}个/轮)")
|
||||
if publish_on:
|
||||
parts.append(f"发布 (每 {int(p_min)}-{int(p_max)} 分钟)")
|
||||
if reply_on:
|
||||
parts.append(f"回复 (每 {int(r_min)}-{int(r_max)} 分钟, 每轮≤{int(max_replies_per_run)}条)")
|
||||
|
||||
_auto_log_append(f"调度器已启动: {' + '.join(parts)}")
|
||||
return f"✅ 自动化已启动 🟢\n任务: {' | '.join(parts)}\n\n💡 点击「刷新日志」查看实时进度"
|
||||
@ -1601,7 +1861,7 @@ with gr.Blocks(
|
||||
with gr.Tab("🤖 自动运营"):
|
||||
gr.Markdown(
|
||||
"### 🤖 无人值守自动化运营\n"
|
||||
"> 一键评论引流 + 一键内容发布 + 随机定时全自动\n\n"
|
||||
"> 一键评论引流 + 一键回复粉丝 + 一键内容发布 + 随机定时全自动\n\n"
|
||||
"⚠️ **注意**: 请确保已连接 LLM、SD WebUI 和 MCP 服务"
|
||||
)
|
||||
|
||||
@ -1623,6 +1883,34 @@ with gr.Blocks(
|
||||
)
|
||||
auto_comment_result = gr.Markdown("")
|
||||
|
||||
gr.Markdown("---")
|
||||
gr.Markdown("#### <20> 一键自动点赞")
|
||||
gr.Markdown(
|
||||
"> 搜索笔记 → 随机选择多篇 → 依次点赞\n"
|
||||
"提升账号活跃度,无需 LLM"
|
||||
)
|
||||
auto_like_count = gr.Number(
|
||||
label="单次点赞数量", value=5, minimum=1, maximum=20,
|
||||
)
|
||||
btn_auto_like = gr.Button(
|
||||
"👍 一键点赞 (单次)", variant="primary", size="lg",
|
||||
)
|
||||
auto_like_result = gr.Markdown("")
|
||||
|
||||
gr.Markdown("---")
|
||||
gr.Markdown("#### <20>💌 一键自动回复")
|
||||
gr.Markdown(
|
||||
"> 扫描我的所有笔记 → 找到粉丝评论 → AI 生成回复 → 逐条发送\n"
|
||||
"自动跳过自己的评论,模拟真人间隔回复"
|
||||
)
|
||||
auto_reply_max = gr.Number(
|
||||
label="单次最多回复条数", value=5, minimum=1, maximum=20,
|
||||
)
|
||||
btn_auto_reply = gr.Button(
|
||||
"💌 一键回复 (单次)", variant="primary", size="lg",
|
||||
)
|
||||
auto_reply_result = gr.Markdown("")
|
||||
|
||||
gr.Markdown("---")
|
||||
gr.Markdown("#### 🚀 一键智能发布")
|
||||
gr.Markdown(
|
||||
@ -1659,6 +1947,36 @@ with gr.Blocks(
|
||||
label="评论最大间隔(分钟)", value=45, minimum=10,
|
||||
)
|
||||
|
||||
with gr.Group():
|
||||
sched_like_on = gr.Checkbox(
|
||||
label="✅ 启用自动点赞", value=True,
|
||||
)
|
||||
with gr.Row():
|
||||
sched_l_min = gr.Number(
|
||||
label="点赞最小间隔(分钟)", value=10, minimum=3,
|
||||
)
|
||||
sched_l_max = gr.Number(
|
||||
label="点赞最大间隔(分钟)", value=30, minimum=5,
|
||||
)
|
||||
sched_like_count = gr.Number(
|
||||
label="每轮点赞数量", value=5, minimum=1, maximum=15,
|
||||
)
|
||||
|
||||
with gr.Group():
|
||||
sched_reply_on = gr.Checkbox(
|
||||
label="✅ 启用自动回复评论", value=True,
|
||||
)
|
||||
with gr.Row():
|
||||
sched_r_min = gr.Number(
|
||||
label="回复最小间隔(分钟)", value=20, minimum=5,
|
||||
)
|
||||
sched_r_max = gr.Number(
|
||||
label="回复最大间隔(分钟)", value=60, minimum=10,
|
||||
)
|
||||
sched_reply_max = gr.Number(
|
||||
label="每轮最多回复条数", value=3, minimum=1, maximum=10,
|
||||
)
|
||||
|
||||
with gr.Group():
|
||||
sched_publish_on = gr.Checkbox(
|
||||
label="✅ 启用自动发布", value=True,
|
||||
@ -1886,6 +2204,16 @@ with gr.Blocks(
|
||||
inputs=[auto_comment_keywords, mcp_url, llm_model, persona],
|
||||
outputs=[auto_comment_result, auto_log_display],
|
||||
)
|
||||
btn_auto_like.click(
|
||||
fn=_auto_like_with_log,
|
||||
inputs=[auto_comment_keywords, auto_like_count, mcp_url],
|
||||
outputs=[auto_like_result, auto_log_display],
|
||||
)
|
||||
btn_auto_reply.click(
|
||||
fn=_auto_reply_with_log,
|
||||
inputs=[auto_reply_max, mcp_url, llm_model, persona],
|
||||
outputs=[auto_reply_result, auto_log_display],
|
||||
)
|
||||
btn_auto_publish.click(
|
||||
fn=_auto_publish_with_log,
|
||||
inputs=[auto_publish_topics, mcp_url, sd_url, sd_model, llm_model],
|
||||
@ -1893,8 +2221,10 @@ with gr.Blocks(
|
||||
)
|
||||
btn_start_sched.click(
|
||||
fn=start_scheduler,
|
||||
inputs=[sched_comment_on, sched_publish_on,
|
||||
inputs=[sched_comment_on, sched_publish_on, sched_reply_on, sched_like_on,
|
||||
sched_c_min, sched_c_max, sched_p_min, sched_p_max,
|
||||
sched_r_min, sched_r_max, sched_reply_max,
|
||||
sched_l_min, sched_l_max, sched_like_count,
|
||||
auto_comment_keywords, auto_publish_topics,
|
||||
mcp_url, sd_url, sd_model, llm_model, persona],
|
||||
outputs=[sched_result],
|
||||
|
||||
@ -272,6 +272,61 @@ class MCPClient:
|
||||
return []
|
||||
return self._parse_feed_entries(result.get("text", ""))
|
||||
|
||||
@staticmethod
|
||||
def _parse_comments(text: str) -> list[dict]:
|
||||
"""从笔记详情文本中解析评论列表为结构化数据
|
||||
|
||||
返回: [{comment_id, user_id, nickname, content, sub_comment_count}, ...]
|
||||
"""
|
||||
comments = []
|
||||
|
||||
# 方式1: 尝试 JSON 解析
|
||||
try:
|
||||
data = json.loads(text)
|
||||
raw_comments = []
|
||||
if isinstance(data, dict):
|
||||
raw_comments = data.get("comments", [])
|
||||
elif isinstance(data, list):
|
||||
raw_comments = data
|
||||
|
||||
for c in raw_comments:
|
||||
user_info = c.get("userInfo") or c.get("user") or {}
|
||||
comments.append({
|
||||
"comment_id": c.get("id", c.get("commentId", "")),
|
||||
"user_id": user_info.get("userId", user_info.get("user_id", "")),
|
||||
"nickname": user_info.get("nickname", user_info.get("nickName", "未知")),
|
||||
"content": c.get("content", ""),
|
||||
"sub_comment_count": c.get("subCommentCount", 0),
|
||||
})
|
||||
if comments:
|
||||
return comments
|
||||
except (json.JSONDecodeError, TypeError, AttributeError):
|
||||
pass
|
||||
|
||||
# 方式2: 正则提取 —— 适配多种 MCP 文本格式
|
||||
# 格式举例: "评论ID: xxx | 用户: xxx (userId) | 内容: xxx"
|
||||
# 或者: 用户名(@nickname): 评论内容
|
||||
comment_ids = re.findall(
|
||||
r'(?:comment_?[Ii]d|评论ID|评论id|"id")["\s::]+([0-9a-f]{24})', text, re.I)
|
||||
user_ids = re.findall(
|
||||
r'(?:user_?[Ii]d|userId|用户ID)["\s::]+([0-9a-f]{24})', text, re.I)
|
||||
nicknames = re.findall(
|
||||
r'(?:nickname|昵称|用户名|用户)["\s::]+([^\n|,]{1,30})', text, re.I)
|
||||
contents = re.findall(
|
||||
r'(?:content|内容|评论内容)["\s::]+([^\n]{1,500})', text, re.I)
|
||||
|
||||
count = max(len(comment_ids), len(contents))
|
||||
for i in range(count):
|
||||
comments.append({
|
||||
"comment_id": comment_ids[i] if i < len(comment_ids) else "",
|
||||
"user_id": user_ids[i] if i < len(user_ids) else "",
|
||||
"nickname": (nicknames[i].strip() if i < len(nicknames) else ""),
|
||||
"content": (contents[i].strip() if i < len(contents) else ""),
|
||||
"sub_comment_count": 0,
|
||||
})
|
||||
|
||||
return comments
|
||||
|
||||
# ---------- 帖子详情 ----------
|
||||
|
||||
def get_feed_detail(self, feed_id: str, xsec_token: str,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user