📝 docs(project): 添加开源社区标准文档与 CI 工作流
- 新增 GitHub Issue 模板(Bug 报告、功能请求)和 Pull Request 模板 - 新增 Code of Conduct(贡献者行为准则)和 Security Policy(安全政策) - 新增 CI 工作流(GitHub Actions),包含 ruff 代码检查和导入验证 - 新增开发依赖文件 requirements-dev.txt 📦 build(ci): 配置 GitHub Actions 持续集成 - 在 push 到 main 分支和 pull request 时自动触发 CI - 添加 lint 任务执行 ruff 代码风格检查 - 添加 import-check 任务验证核心服务模块导入 ♻️ refactor(structure): 重构项目目录结构 - 将根目录的 6 个服务模块迁移至 services/ 包 - 更新所有相关文件的导入语句(main.py、ui/、services/) - 根目录仅保留 main.py 作为唯一 Python 入口文件 🔧 chore(config): 调整配置和资源文件路径 - 将 config.json 移至 config/ 目录,更新相关引用 - 将个人头像图片移至 assets/faces/ 目录,更新 .gitignore - 更新 Dockerfile 和 docker-compose.yml 中的配置路径 📝 docs(readme): 完善 README 文档 - 添加项目状态徽章(Python 版本、License、CI) - 更新项目结构图反映实际目录布局 - 修正使用指南中的 Tab 名称和操作路径 - 替换 your-username 占位符为格式提示 🗑️ chore(cleanup): 清理冗余文件 - 删除旧版备份文件、测试脚本、临时记录和运行日志 - 删除散落的个人图片文件(已归档至 assets/faces/)
This commit is contained in:
parent
b5deafa2cc
commit
2ba87c8f6e
37
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
37
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
---
|
||||
name: Bug 报告
|
||||
about: 报告一个问题,帮助我们改进
|
||||
title: "[BUG] "
|
||||
labels: bug
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
## 问题描述
|
||||
|
||||
请简洁清晰地描述遇到的问题。
|
||||
|
||||
## 复现步骤
|
||||
|
||||
1. 进入 '...'
|
||||
2. 点击 '...'
|
||||
3. 滚动至 '...'
|
||||
4. 出现错误
|
||||
|
||||
## 预期行为
|
||||
|
||||
描述你预期应该发生什么。
|
||||
|
||||
## 实际行为
|
||||
|
||||
描述实际发生了什么,请附上错误截图或日志信息。
|
||||
|
||||
## 环境信息
|
||||
|
||||
- **操作系统**:(例如 Windows 11 / macOS 14 / Ubuntu 22.04)
|
||||
- **Python 版本**:(执行 `python --version` 获取)
|
||||
- **autobot 版本/提交**:(执行 `git rev-parse --short HEAD` 获取)
|
||||
- **相关依赖版本**:(如 gradio、openai 等)
|
||||
|
||||
## 附加信息
|
||||
|
||||
任何其他与问题相关的上下文、截图或日志文件,请粘贴于此。
|
||||
23
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
23
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
---
|
||||
name: 功能请求
|
||||
about: 为本项目提出新点子或改进建议
|
||||
title: "[FEATURE] "
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
## 背景与需求
|
||||
|
||||
请描述你目前遇到的问题或痛点。例如:"当我想要 [...] 时,总是感到不便,因为 [...]"
|
||||
|
||||
## 期望的解决方案
|
||||
|
||||
请清晰描述你希望实现的功能或行为。
|
||||
|
||||
## 替代方案
|
||||
|
||||
你是否考虑过其他解决方案或变通方法?请描述。
|
||||
|
||||
## 附加信息
|
||||
|
||||
你可以在此添加任何截图、参考链接或其他有助于理解该功能请求的上下文信息。
|
||||
35
.github/pull_request_template.md
vendored
Normal file
35
.github/pull_request_template.md
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
## 变更类型
|
||||
|
||||
请勾选适用的选项(在 `[ ]` 中填入 `x`):
|
||||
|
||||
- [ ] 🐛 Bug Fix(修复问题,未破坏现有功能)
|
||||
- [ ] ✨ Feature(新功能,未破坏现有功能)
|
||||
- [ ] 📝 Docs(仅文档变更)
|
||||
- [ ] ♻️ Refactor(代码重构,未修复 Bug 也未新增功能)
|
||||
- [ ] 🎨 Style(格式化、缩进等,不影响代码逻辑)
|
||||
- [ ] ⚡ Performance(性能优化)
|
||||
- [ ] 🔧 Chore(构建流程、工具配置等杂项变更)
|
||||
|
||||
## 变更描述
|
||||
|
||||
请简洁描述本 PR 做了什么,以及为什么需要这些变更。
|
||||
|
||||
## 相关 Issue
|
||||
|
||||
关闭 #(填入 Issue 编号,若无可删除此行)
|
||||
|
||||
## 测试说明
|
||||
|
||||
请描述你如何测试了本次变更(手动测试步骤、自动化测试等):
|
||||
|
||||
- [ ] 我在本地运行了 `python main.py` 确认应用正常启动
|
||||
- [ ] 我验证了受影响的功能仍按预期工作
|
||||
- [ ] 我运行了 `ruff check .` 确认无新增 lint 错误
|
||||
|
||||
## 截图(如适用)
|
||||
|
||||
如果本 PR 包含 UI 变更,请附上前后对比截图。
|
||||
|
||||
## 备注
|
||||
|
||||
有任何需要 reviewer 特别关注的地方,请在此说明。
|
||||
48
.github/workflows/ci.yml
vendored
Normal file
48
.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: Lint (ruff)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.10"
|
||||
|
||||
- name: Install ruff
|
||||
run: pip install ruff>=0.4.0
|
||||
|
||||
- name: Run ruff
|
||||
run: ruff check . --select E,F,W --ignore E501,E402,W291,W293
|
||||
|
||||
import-check:
|
||||
name: Import Check
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.10"
|
||||
|
||||
- name: Install dependencies
|
||||
run: pip install -r requirements.txt
|
||||
|
||||
- name: Verify core service imports
|
||||
run: |
|
||||
python -c "from services.config_manager import ConfigManager; print('config_manager OK')"
|
||||
python -c "from services.llm_service import LLMService; print('llm_service OK')"
|
||||
python -c "from services.mcp_client import get_mcp_client; print('mcp_client OK')"
|
||||
python -c "from services.analytics_service import AnalyticsService; print('analytics_service OK')"
|
||||
python -c "from services.publish_queue import STATUS_LABELS; print('publish_queue OK')"
|
||||
python -c "from services.sd_service import SDService; print('sd_service OK')"
|
||||
9
.gitignore
vendored
9
.gitignore
vendored
@ -16,7 +16,7 @@ venv/
|
||||
env/
|
||||
|
||||
# ========== 敏感配置 ==========
|
||||
config.json
|
||||
config/config.json
|
||||
cookies.json
|
||||
*.cookie
|
||||
|
||||
@ -43,3 +43,10 @@ config copy.json
|
||||
# ========== 临时文件 ==========
|
||||
*.tmp
|
||||
*.bak
|
||||
|
||||
# ========== 个人媒体资产(隐私,不入版本控制) ==========
|
||||
assets/faces/
|
||||
|
||||
# ========== 自动生成的启动脚本(含机器绝对路径) ==========
|
||||
scripts/_autostart.bat
|
||||
scripts/_autostart.vbs
|
||||
83
CODE_OF_CONDUCT.md
Normal file
83
CODE_OF_CONDUCT.md
Normal file
@ -0,0 +1,83 @@
|
||||
# 贡献者行为准则
|
||||
|
||||
## 我们的承诺
|
||||
|
||||
作为成员、贡献者和领导者,我们承诺让每一位参与者都能在无骚扰的环境中参与我们的社区,不论其年龄、体型、是否有可见或不可见的残疾、族裔、性别特征、性别认同与表达、经验水平、教育程度、社会经济地位、国籍、外表、种族、种姓、肤色、宗教信仰或性取向。
|
||||
|
||||
我们承诺以有助于建立开放、友好、多元、包容和健康社区的方式行事和互动。
|
||||
|
||||
## 我们的准则
|
||||
|
||||
有助于为我们的社区创造积极环境的行为示例包括:
|
||||
|
||||
* 对他人表现出同理心和善意
|
||||
* 尊重不同的意见、观点和经历
|
||||
* 给予并优雅地接受建设性反馈
|
||||
* 承担责任并向受我们错误影响的人道歉,并从经历中学习
|
||||
* 关注整个社区的最大利益,不只是我们个人的利益
|
||||
|
||||
不可接受的行为示例包括:
|
||||
|
||||
* 使用性化语言或图像,以及任何形式的性关注或性骚扰
|
||||
* 发表挑衅性、侮辱性或贬义的评论,以及针对个人或政治的攻击
|
||||
* 公开或私下骚扰
|
||||
* 未经明确许可,发布他人的私人信息,例如实际地址或电子邮件地址
|
||||
* 在专业环境中其他可被合理认为不适当的行为
|
||||
|
||||
## 执行责任
|
||||
|
||||
社区领导者有责任阐明和执行我们可接受行为的准则,并在遇到任何他们认为不适当、有威胁、冒犯或有害的行为时采取适当且公平的纠正行动。
|
||||
|
||||
社区领导者有权利和责任删除、编辑或拒绝与本行为准则不符的评论、提交、代码、wiki 编辑、议题及其他贡献,并在适当时就审核决定的原因进行沟通。
|
||||
|
||||
## 适用范围
|
||||
|
||||
本行为准则适用于所有社区空间,也适用于当个人在公共空间中正式代表本社区时的情形。代表我们社区的示例包括:使用官方电子邮件地址、通过官方社交媒体账号发帖,或在线上或线下活动中担任指定代表。
|
||||
|
||||
## 执行
|
||||
|
||||
如遇到骚扰、滥用或其他不可接受的行为,可通过 GitHub Issues 或在本仓库的 Discussions 中向社区维护者报告。所有投诉都将被迅速、公平地审查和调查。
|
||||
|
||||
所有社区领导者有义务尊重任何事件报告者的隐私和安全。
|
||||
|
||||
## 执行指南
|
||||
|
||||
社区领导者将遵循以下社区影响指南,确定对违反本行为准则的任何行为的后果:
|
||||
|
||||
### 1. 纠正
|
||||
|
||||
**社区影响**:使用不当语言或其他被认为不专业或社区不欢迎的行为。
|
||||
|
||||
**后果**:社区领导者会发出私人书面警告,说明违规的性质并解释为何该行为不当。可能会要求公开道歉。
|
||||
|
||||
### 2. 警告
|
||||
|
||||
**社区影响**:单次事件或一系列行为的违规。
|
||||
|
||||
**后果**:警告并说明持续行为的后果。在指定时间内不得与相关人员互动,包括主动与执行本行为准则的人员互动。这包括避免在社区空间以及社交媒体等外部渠道进行互动。违反这些条款可能会导致临时或永久封禁。
|
||||
|
||||
### 3. 临时封禁
|
||||
|
||||
**社区影响**:严重违反社区准则,包括持续的不当行为。
|
||||
|
||||
**后果**:在指定时间内临时禁止与社区进行任何形式的互动或公开通信。在此期间,不允许与相关人员进行任何公开或私下的互动,包括主动与执行本行为准则的人员互动。违反这些条款可能会导致永久封禁。
|
||||
|
||||
### 4. 永久封禁
|
||||
|
||||
**社区影响**:表现出违反社区准则的模式,包括持续的不当行为、对某个人的骚扰或对某类人群的攻击或诋毁。
|
||||
|
||||
**后果**:永久禁止在社区内进行任何形式的公开互动。
|
||||
|
||||
## 归属
|
||||
|
||||
本行为准则改编自 [Contributor Covenant][homepage] v2.1 版,详情请访问 [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]。
|
||||
|
||||
社区影响指南的灵感来自 [Mozilla 的行为准则执行阶梯][Mozilla CoC]。
|
||||
|
||||
有关本行为准则常见问题的解答,请参阅 [https://www.contributor-covenant.org/faq][FAQ]。其他语言的翻译请参阅 [https://www.contributor-covenant.org/translations][translations]。
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
|
||||
[Mozilla CoC]: https://github.com/mozilla/diversity
|
||||
[FAQ]: https://www.contributor-covenant.org/faq
|
||||
[translations]: https://www.contributor-covenant.org/translations
|
||||
@ -28,12 +28,13 @@ RUN apt-get update && \
|
||||
COPY --from=builder /install /usr/local
|
||||
|
||||
# 复制项目代码
|
||||
COPY config_manager.py llm_service.py sd_service.py mcp_client.py main.py ./
|
||||
COPY requirements.txt ./
|
||||
COPY config.example.json ./
|
||||
COPY main.py requirements.txt ./
|
||||
COPY services/ services/
|
||||
COPY ui/ ui/
|
||||
COPY config/config.example.json config/config.example.json
|
||||
|
||||
# 创建工作目录
|
||||
RUN mkdir -p xhs_workspace
|
||||
RUN mkdir -p xhs_workspace config logs
|
||||
|
||||
# Gradio 默认端口
|
||||
EXPOSE 7860
|
||||
|
||||
82
README.md
82
README.md
@ -13,6 +13,11 @@
|
||||
<a href="#常见问题">FAQ</a> •
|
||||
<a href="#贡献指南">贡献</a>
|
||||
</p>
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/python-3.10+-blue" alt="Python">
|
||||
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
|
||||
<img src="https://github.com/<your-github-username>/xhs-autobot/actions/workflows/ci.yml/badge.svg" alt="CI">
|
||||
</p>
|
||||
</p>
|
||||
|
||||
---
|
||||
@ -91,7 +96,7 @@
|
||||
|
||||
```bash
|
||||
# 1. 克隆项目
|
||||
git clone https://github.com/your-username/xhs-autobot.git
|
||||
git clone https://github.com/<your-github-username>/xhs-autobot.git
|
||||
cd xhs-autobot
|
||||
|
||||
# 2. 创建虚拟环境(推荐)
|
||||
@ -105,8 +110,8 @@ source .venv/bin/activate
|
||||
pip install -r requirements.txt
|
||||
|
||||
# 4. 复制配置文件并填写你的 API Key
|
||||
cp config.example.json config.json
|
||||
# 编辑 config.json,填写 api_key、base_url 等
|
||||
cp config/config.example.json config/config.json
|
||||
# 编辑 config/config.json,填写 api_key、base_url 等
|
||||
|
||||
# 5. 启动!
|
||||
python main.py
|
||||
@ -120,12 +125,12 @@ python main.py
|
||||
|
||||
```bash
|
||||
# 1. 克隆项目
|
||||
git clone https://github.com/your-username/xhs-autobot.git
|
||||
git clone https://github.com/<your-github-username>/xhs-autobot.git
|
||||
cd xhs-autobot
|
||||
|
||||
# 2. 准备配置文件
|
||||
cp config.example.json config.json
|
||||
# 编辑 config.json,填写 api_key、base_url 等
|
||||
cp config/config.example.json config/config.json
|
||||
# 编辑 config/config.json,填写 api_key、base_url 等
|
||||
# ⚠️ mcp_url 改为容器网络地址:
|
||||
# "mcp_url": "http://xhs-mcp:18060/mcp"
|
||||
|
||||
@ -164,7 +169,7 @@ docker compose exec xhs-autobot bash
|
||||
|
||||
#### 启用 Stable Diffusion(需要 NVIDIA GPU)
|
||||
|
||||
编辑 `docker-compose.yml`,取消 `sd-webui` 部分的注释,并将 `config.json` 中的 `sd_url` 改为:
|
||||
编辑 `docker-compose.yml`,取消 `sd-webui` 部分的注释,并将 `config/config.json` 中的 `sd_url` 改为:
|
||||
|
||||
```json
|
||||
"sd_url": "http://sd-webui:7860"
|
||||
@ -205,11 +210,11 @@ python launch.py --api
|
||||
|
||||
### 首次使用流程
|
||||
|
||||
1. **配置 LLM** — 展开「⚙️ 全局设置」,添加 LLM 提供商(API Key + Base URL),点击「连接 LLM」
|
||||
2. **连接 SD**(可选)— 填写 SD WebUI URL,点击「连接 SD」
|
||||
1. **配置 LLM** — 切换到「⚙️ 配置」 Tab,添加 LLM 提供商(API Key + Base URL),点击「连接 LLM」
|
||||
2. **连接 SD**(可选)— 在「⚙️ 配置」 Tab 填写 SD WebUI URL,点击「连接 SD」
|
||||
3. **检查 MCP** — 点击「检查 MCP」确认小红书服务正常
|
||||
4. **登录小红书** — 切换到「🔐 账号登录」 Tab,扫码登录
|
||||
5. **选择人设** — 在人设下拉框选择博主人设(影响文案风格 + 图片视觉)
|
||||
5. **选择人设** — 在「⚙️ 配置」 Tab 的人设下拉框选择博主人设(影响文案风格 + 图片视觉)
|
||||
6. **开始创作** — 切换到「✨ 内容创作」Tab,输入主题,一键生成
|
||||
|
||||
### 自动化运营
|
||||
@ -243,7 +248,7 @@ python launch.py --api
|
||||
|
||||
## ⚙️ 配置说明
|
||||
|
||||
配置文件 `config.json` 会在运行时自动创建和保存。首次使用请从 `config.example.json` 复制:
|
||||
配置文件 `config/config.json` 会在运行时自动创建和保存。首次使用请从 `config/config.example.json` 复制:
|
||||
|
||||
```json
|
||||
{
|
||||
@ -280,24 +285,47 @@ python launch.py --api
|
||||
|
||||
```
|
||||
xhs-autobot/
|
||||
├── main.py # 主程序入口 (Gradio UI + 业务逻辑 + 8 个 Tab)
|
||||
├── config_manager.py # 配置管理模块 (单例、自动保存)
|
||||
├── llm_service.py # LLM 服务封装 (文案生成、热点分析、评论回复、SD Prompt 指南)
|
||||
├── sd_service.py # Stable Diffusion 服务封装 (3 模型适配、9 人设视觉方案)
|
||||
├── mcp_client.py # 小红书 MCP 客户端 (搜索、发布、评论、点赞)
|
||||
├── analytics_service.py # 笔记数据分析 & 权重学习服务
|
||||
├── publish_queue.py # 内容排期队列 (SQLite + 后台 Publisher)
|
||||
├── config.json # 运行时配置 (gitignore)
|
||||
├── config.example.json # 配置模板
|
||||
├── main.py # 主程序入口 (Gradio UI + 事件绑定)
|
||||
├── config/ # 配置文件目录
|
||||
│ ├── config.json # 运行时配置 (gitignore)
|
||||
│ └── config.example.json # 配置模板
|
||||
├── logs/ # 运行日志 (gitignore)
|
||||
│ └── autobot.log
|
||||
├── docs/ # 参考文档
|
||||
│ └── mcp.md # xiaohongshu-mcp 参考文档
|
||||
├── scripts/ # 运行时生成的启动脚本 (gitignore)
|
||||
│ ├── _autostart.bat
|
||||
│ └── _autostart.vbs
|
||||
├── requirements.txt # Python 依赖
|
||||
├── requirements-dev.txt # 开发工具依赖 (ruff 等)
|
||||
├── Dockerfile # Docker 镜像构建
|
||||
├── docker-compose.yml # Docker Compose 编排
|
||||
├── .dockerignore # Docker 构建排除规则
|
||||
├── xhs_workspace/ # 导出的文案和图片 (gitignore)
|
||||
│ ├── publish_queue.db # 排期队列数据库
|
||||
│ ├── analytics_data.json # 笔记表现数据
|
||||
│ └── content_weights.json # 内容权重数据
|
||||
└── autobot.log # 运行日志 (gitignore)
|
||||
├── services/ # 业务逻辑层
|
||||
│ ├── config_manager.py # 配置管理模块 (单例、自动保存)
|
||||
│ ├── llm_service.py # LLM 服务封装 (文案生成、热点分析等)
|
||||
│ ├── sd_service.py # Stable Diffusion 服务封装 (3 模型适配、9 人设视觉方案)
|
||||
│ ├── mcp_client.py # 小红书 MCP 客户端 (搜索、发布、评论、点赞)
|
||||
│ ├── analytics_service.py # 笔记数据分析 & 权重学习服务
|
||||
│ ├── publish_queue.py # 内容排期队列 (SQLite + 后台 Publisher)
|
||||
│ ├── scheduler.py # 自动化运营调度器
|
||||
│ ├── content.py # 文案生成、图片生成、导出、发布
|
||||
│ ├── hotspot.py # 热点探测、热点生成、笔记缓存
|
||||
│ ├── engagement.py # 评论管家:手动评论、回复、互动
|
||||
│ ├── profile.py # 小红书账号 Profile 解析与可视化
|
||||
│ ├── persona.py # 人设管理:常量、关键词池、主题池
|
||||
│ ├── connection.py # LLM/SD/MCP/XHS 连接管理
|
||||
│ ├── queue_ops.py # 发布队列操作
|
||||
│ ├── rate_limiter.py # 频率控制、每日限额、冷却管理
|
||||
│ └── autostart.py # Windows 开机自启管理
|
||||
├── ui/ # Gradio UI 层
|
||||
│ ├── app.py # 主界面构建函数(全部 Tab UI + 事件绑定)
|
||||
│ └── tab_create.py # Tab 1「✨ 内容创作」组件定义
|
||||
├── assets/
|
||||
│ └── faces/ # 头像文件目录 (gitignore)
|
||||
└── xhs_workspace/ # 导出的文案和图片 (gitignore)
|
||||
├── publish_queue.db # 排期队列数据库
|
||||
├── analytics_data.json # 笔记表现数据
|
||||
└── content_weights.json # 内容权重数据
|
||||
```
|
||||
|
||||
---
|
||||
@ -371,7 +399,7 @@ xhs-autobot/
|
||||
<details>
|
||||
<summary><b>Q: 如何添加自定义人设?</b></summary>
|
||||
|
||||
在人设下拉框中直接输入自定义人设描述即可(支持自由输入)。如需配套主题池和关键词,需在 `main.py` 的 `PERSONA_POOL_MAP` 中添加对应条目。如需配套 SD 视觉方案,需在 `sd_service.py` 的 `PERSONA_SD_PROFILES` 中添加。
|
||||
在人设下拉框中直接输入自定义人设描述即可(支持自由输入)。如需配套主题池和关键词,需在 `services/persona.py` 的 `PERSONA_POOL_MAP` 中添加对应条目。如需配套 SD 视觉方案,需在 `services/sd_service.py` 的 `PERSONA_SD_PROFILES` 中添加。
|
||||
</details>
|
||||
|
||||
<details>
|
||||
|
||||
36
SECURITY.md
Normal file
36
SECURITY.md
Normal file
@ -0,0 +1,36 @@
|
||||
# 安全政策
|
||||
|
||||
## 支持的版本
|
||||
|
||||
目前我们对以下版本提供安全更新支持:
|
||||
|
||||
| 版本 | 支持状态 |
|
||||
| ---- | -------- |
|
||||
| main 分支(最新提交) | ✅ 支持 |
|
||||
| 旧版本 / 存档提交 | ❌ 不支持 |
|
||||
|
||||
## 报告安全漏洞
|
||||
|
||||
**请勿通过公开的 GitHub Issues 上报安全漏洞。**
|
||||
|
||||
如果您发现了安全漏洞,请通过以下方式私下联系我们:
|
||||
|
||||
1. **GitHub Security Advisory(推荐)**:访问本仓库的 [Security → Advisories](../../security/advisories/new) 页面,点击「Report a vulnerability」提交私密报告。
|
||||
|
||||
2. **私信维护者**:如无法使用上述方式,可通过 GitHub 私信联系仓库维护者。
|
||||
|
||||
### 报告内容请包含
|
||||
|
||||
- 漏洞的详细描述
|
||||
- 影响范围(受影响的版本、功能模块)
|
||||
- 复现步骤(如可能)
|
||||
- 潜在影响评估
|
||||
- 您建议的修复方案(如有)
|
||||
|
||||
## 响应承诺
|
||||
|
||||
- 我们会在 **7 个工作日内** 确认收到您的报告
|
||||
- 我们会在 **30 天内** 评估漏洞并提供修复时间表
|
||||
- 修复发布后,我们会在 Release Notes 中公开致谢(如您同意)
|
||||
|
||||
感谢您帮助保持本项目的安全!
|
||||
1
Todo.md
1
Todo.md
@ -1 +0,0 @@
|
||||
目前的脚本已经实现了从 “灵感 -> 文案 -> 绘图 -> 发布” 的核心闭环,作为一个个人辅助工具(MVP,最小可行性产品)已经非常出色了。但是,如果要作为一个专业的运营工具或者满足商业化需求,目前的版本还存在明显的短板。我将从 内容质量、运营闭环、账号安全、功能深度 四个维度为你进行全面分析,并给出升级建议。📊 当前功能评分表维度当前得分评价核心流程⭐⭐⭐⭐⭐流程跑通,无需在多个软件间切换,效率极大提升。内容质量⭐⭐⭐LLM 文案通用性强但个性不足;SD 绘图仅支持基础生图,缺乏精细控制。运营功能⭐⭐仅支持“发”,缺乏“看”(数据分析)和“回”(评论互动)。多媒体能力⭐⭐仅支持图片,不支持视频(尽管 MCP 支持)。稳定性⭐⭐⭐依赖本地环境和 Cookie 有效期,缺乏异常重试和账号管理。🔍 深度差距分析与改进建议1. 视觉能力的局限性 (痛点:图片不可控)目前使用的是基础的 txt2img(文生图)。问题:很难控制人物姿势、保持角色一致性(比如同一个博主IP)、或者在特定背景中植入产品。缺口:ControlNet 支持:无法指定姿势(Openpose)或线稿上色(Canny)。LoRA 切换:无法快速切换画风(如:二次元 vs 真实感 vs 胶片风)。Img2Img:无法基于参考图进行修改。💡 改进建议:在 UI 中增加 ControlNet 参数接口,或者增加“风格预设”下拉框(后台自动切换 LoRA)。2. 缺乏“选题与热点”辅助 (痛点:不知道写什么)目前主要依赖用户自己输入“主题”。问题:如果用户不知道最近什么火,写的文章可能没人看。缺口:MCP 搜索能力未利用:xiaohongshu-mcp 有 search_feeds 功能,但脚本里没用。💡 改进建议:增加 “热点探测” tab。逻辑:用户输入关键词 -> 调用 MCP 搜索 -> LLM 分析热门笔记的标题和结构 -> 生成“爆款模仿”方案。3. 缺失视频支持 (痛点:视频流量更大)小红书目前对视频流量扶持很大。问题:目前的 UI 和逻辑只支持图片。虽然 MCP 支持 publish_with_video,但你没接。💡 改进建议:UI 增加“上传视频”或“AI 生成视频”入口。接入 Runway / Luma API 或本地 AnimateDiff 生成几秒的动态视频。4. 运营互动的缺失 (痛点:发完不管)运营小红书,“养号”和“回复” 与发帖一样重要。问题:目前是“射后不理”。缺口:评论管理:无法自动回复评论,无法引导私域。数据反馈:发出去的笔记有多少阅读?脚本里看不到。💡 改进建议:增加 “评论管家” 模块:定期调用 MCP 获取新评论 -> LLM 生成回复 -> 调用 MCP 回复。增加 “数据看板”:调用 user_profile 展示昨日点赞涨粉数。5. 账号矩阵与安全性 (痛点:单点风险)问题:目前是单账号模式。缺口:多账号切换:如果我有 5 个号,需要反复手动扫码或替换 Cookie 文件。定时发布:只能“立即发布”。真正运营需要设定在晚高峰(18:00-21:00)自动发。💡 改进建议:引入简单的 SQLite 数据库或 JSON 文件管理多组 Cookie。引入 APScheduler 库,实现“存入草稿箱,特定时间自动调用 MCP 发布”。🛠️ 下一步升级路线图 (Roadmap)如果你想把这个脚本升级为V2.0 专业版,建议按以下顺序添加功能:第一阶段:补全 MCP 能力 (低成本,高回报)接入搜索功能:在写文案前,先让 AI 看 5 篇同类热门笔记。接入数据面板:在侧边栏显示当前账号粉丝数、获赞数。第二阶段:增强视觉 (提升内容力)SD 进阶:支持上传参考图 (img2img)。本地图库:有时候不想用 AI 图,想混排自己拍的照片,增加“本地上传”按钮。第三阶段:自动化运营 (解放双手)自动回复机器人:根据设定的人设(知性姐姐/毒舌博主)自动回评论。定时任务:设置一个队列,让它自己跑。
|
||||
@ -1,3 +0,0 @@
|
||||
@echo off
|
||||
cd /d "F:\3_Personal\AI\xhs_bot\autobot"
|
||||
"F:\3_Personal\AI\xhs_bot\autobot\.venv\Scripts\pythonw.exe" "F:\3_Personal\AI\xhs_bot\autobot\main.py"
|
||||
@ -1,3 +0,0 @@
|
||||
Set WshShell = CreateObject("WScript.Shell")
|
||||
WshShell.Run chr(34) & "F:\3_Personal\AI\xhs_bot\autobot\_autostart.bat" & chr(34), 0
|
||||
Set WshShell = Nothing
|
||||
@ -1,38 +0,0 @@
|
||||
"""
|
||||
测试配置保存和加载功能
|
||||
"""
|
||||
import json
|
||||
from config_manager import ConfigManager
|
||||
|
||||
# 测试配置保存
|
||||
cfg = ConfigManager()
|
||||
|
||||
print("=== 测试LLM提供商切换保存 ===")
|
||||
print(f"当前激活: {cfg.get('active_llm')}")
|
||||
|
||||
# 切换到 wolfai
|
||||
cfg.set_active_llm("wolfai")
|
||||
print(f"切换后: {cfg.get('active_llm')}")
|
||||
|
||||
# 重新加载验证
|
||||
cfg2 = ConfigManager()
|
||||
print(f"重新加载: {cfg2.get('active_llm')}")
|
||||
|
||||
# 检查config.json文件内容
|
||||
with open("config.json", "r", encoding="utf-8") as f:
|
||||
data = json.load(f)
|
||||
print(f"\nconfig.json中的active_llm: {data.get('active_llm')}")
|
||||
print(f"api_key: {data.get('api_key', '')[:20]}...")
|
||||
print(f"base_url: {data.get('base_url')}")
|
||||
|
||||
print("\n=== 测试其他全局设置保存 ===")
|
||||
print(f"当前persona: {cfg.get('persona')}")
|
||||
cfg.set("persona", "测试人设")
|
||||
print(f"修改后persona: {cfg.get('persona')}")
|
||||
|
||||
# 重新验证
|
||||
with open("config.json", "r", encoding="utf-8") as f:
|
||||
data = json.load(f)
|
||||
print(f"config.json中的persona: {data.get('persona')}")
|
||||
|
||||
print("\n✅ 测试完成")
|
||||
BIN
beauty.png
BIN
beauty.png
Binary file not shown.
|
Before Width: | Height: | Size: 6.5 MiB |
@ -1,10 +0,0 @@
|
||||
{
|
||||
"api_key": "sk-d212b926f51f4f0f9297629cd2ab77b4",
|
||||
"base_url": "https://api.deepseek.com/v1",
|
||||
"sd_url": "http://127.0.0.1:7860",
|
||||
"mcp_url": "http://localhost:18060/mcp",
|
||||
"model": "deepseek-reasoner",
|
||||
"persona": "温柔知性的时尚博主",
|
||||
"auto_reply_enabled": false,
|
||||
"schedule_enabled": false
|
||||
}
|
||||
60
config.json
60
config.json
@ -1,60 +0,0 @@
|
||||
{
|
||||
"api_key": "sk-NPZECL5m3BmZv0S9YO9KOd179pepRH08iYeAn1Tk07Jux9Br",
|
||||
"base_url": "https://wolfai.top/v1",
|
||||
"sd_url": "http://127.0.0.1:7861",
|
||||
"mcp_url": "http://localhost:18060/mcp",
|
||||
"model": "gemini-3-flash-preview",
|
||||
"persona": "二次元coser,喜欢分享cos日常和动漫周边",
|
||||
"auto_reply_enabled": false,
|
||||
"schedule_enabled": false,
|
||||
"my_user_id": "69872540000000002303cc42",
|
||||
"active_llm": "wolfai",
|
||||
"llm_providers": [
|
||||
{
|
||||
"name": "默认",
|
||||
"api_key": "sk-d212b926f51f4f0f9297629cd2ab77b4",
|
||||
"base_url": "https://api.deepseek.com/v1"
|
||||
},
|
||||
{
|
||||
"name": "wolfai",
|
||||
"api_key": "sk-NPZECL5m3BmZv0S9YO9KOd179pepRH08iYeAn1Tk07Jux9Br",
|
||||
"base_url": "https://wolfai.top/v1"
|
||||
},
|
||||
{
|
||||
"name": "openrouter",
|
||||
"api_key": "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",
|
||||
"base_url": "https://openrouter.ai/api/v1"
|
||||
}
|
||||
],
|
||||
"use_smart_weights": true,
|
||||
"quality_mode": "精细 (约2-3分钟)",
|
||||
"sd_steps": 35,
|
||||
"sd_cfg_scale": 6,
|
||||
"sd_negative_prompt": "",
|
||||
"sched_comment_on": true,
|
||||
"sched_like_on": true,
|
||||
"sched_fav_on": true,
|
||||
"sched_reply_on": true,
|
||||
"sched_publish_on": true,
|
||||
"sched_c_min": 15,
|
||||
"sched_c_max": 45,
|
||||
"sched_l_min": 10,
|
||||
"sched_l_max": 30,
|
||||
"sched_like_count": 5,
|
||||
"sched_fav_min": 12,
|
||||
"sched_fav_max": 35,
|
||||
"sched_fav_count": 3,
|
||||
"sched_r_min": 20,
|
||||
"sched_r_max": 60,
|
||||
"sched_reply_max": 3,
|
||||
"sched_p_min": 60,
|
||||
"sched_p_max": 180,
|
||||
"sched_start_hour": 8,
|
||||
"sched_end_hour": 23,
|
||||
"auto_like_count": 5,
|
||||
"auto_fav_count": 3,
|
||||
"auto_reply_max": 5,
|
||||
"learn_interval": 6,
|
||||
"queue_gen_count": 3,
|
||||
"xsec_token": "AB1StlX7ffxsEkfyNuTFDesPlV2g1haPcYuh1-AkYcQxo="
|
||||
}
|
||||
@ -11,8 +11,8 @@ services:
|
||||
ports:
|
||||
- "7860:7860"
|
||||
volumes:
|
||||
# 配置文件挂载(首次请从 config.example.json 复制并填写)
|
||||
- ./config.json:/app/config.json
|
||||
# 配置文件挂载(首次请从 config/config.example.json 复制并填写)
|
||||
- ./config/config.json:/app/config/config.json
|
||||
# 工作目录(导出的文案 & 图片)
|
||||
- ./xhs_workspace:/app/xhs_workspace
|
||||
environment:
|
||||
|
||||
15
main.py
15
main.py
@ -17,11 +17,11 @@ from PIL import Image
|
||||
import matplotlib
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from config_manager import ConfigManager, OUTPUT_DIR
|
||||
from llm_service import LLMService
|
||||
from sd_service import SDService, DEFAULT_NEGATIVE, FACE_IMAGE_PATH, SD_PRESET_NAMES, get_sd_preset, get_model_profile, get_model_profile_info, detect_model_profile, SD_MODEL_PROFILES
|
||||
from mcp_client import MCPClient, get_mcp_client
|
||||
from analytics_service import AnalyticsService
|
||||
from services.config_manager import ConfigManager, OUTPUT_DIR
|
||||
from services.llm_service import LLMService
|
||||
from services.sd_service import SDService, DEFAULT_NEGATIVE, FACE_IMAGE_PATH, SD_PRESET_NAMES, get_sd_preset, get_model_profile, get_model_profile_info, detect_model_profile, SD_MODEL_PROFILES
|
||||
from services.mcp_client import MCPClient, get_mcp_client
|
||||
from services.analytics_service import AnalyticsService
|
||||
from ui.tab_create import build_tab
|
||||
|
||||
# ================= matplotlib 中文字体配置 =================
|
||||
@ -37,12 +37,13 @@ plt.rcParams["axes.unicode_minus"] = False
|
||||
|
||||
# ================= 日志配置 =================
|
||||
|
||||
os.makedirs("logs", exist_ok=True)
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
|
||||
handlers=[
|
||||
logging.StreamHandler(),
|
||||
logging.FileHandler("autobot.log", encoding="utf-8"),
|
||||
logging.FileHandler("logs/autobot.log", encoding="utf-8"),
|
||||
],
|
||||
)
|
||||
logger = logging.getLogger("autobot")
|
||||
@ -59,7 +60,7 @@ mcp = get_mcp_client(cfg.get("mcp_url", "http://localhost:18060/mcp"))
|
||||
analytics = AnalyticsService(OUTPUT_DIR)
|
||||
|
||||
# ================= 发布队列 =================
|
||||
from publish_queue import (
|
||||
from services.publish_queue import (
|
||||
PublishQueue, QueuePublisher,
|
||||
STATUS_DRAFT, STATUS_APPROVED, STATUS_SCHEDULED, STATUS_PUBLISHING,
|
||||
STATUS_PUBLISHED, STATUS_FAILED, STATUS_REJECTED, STATUS_LABELS,
|
||||
|
||||
@ -1,264 +0,0 @@
|
||||
import gradio as gr
|
||||
import requests
|
||||
import json
|
||||
import base64
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
import re
|
||||
import shutil
|
||||
import platform
|
||||
import subprocess
|
||||
from PIL import Image
|
||||
|
||||
# ================= 0. 基础配置与工具 =================
|
||||
|
||||
# 强制不走代理连接本地 SD
|
||||
os.environ['NO_PROXY'] = '127.0.0.1,localhost'
|
||||
|
||||
CONFIG_FILE = "config.json"
|
||||
OUTPUT_DIR = "xhs_workspace"
|
||||
os.makedirs(OUTPUT_DIR, exist_ok=True)
|
||||
|
||||
class ConfigManager:
|
||||
@staticmethod
|
||||
def load():
|
||||
if os.path.exists(CONFIG_FILE):
|
||||
try:
|
||||
with open(CONFIG_FILE, 'r', encoding='utf-8') as f:
|
||||
return json.load(f)
|
||||
except:
|
||||
pass
|
||||
return {
|
||||
"api_key": "",
|
||||
"base_url": "https://api.openai.com/v1",
|
||||
"sd_url": "http://127.0.0.1:7860",
|
||||
"model": "gpt-3.5-turbo"
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def save(config_data):
|
||||
with open(CONFIG_FILE, 'w', encoding='utf-8') as f:
|
||||
json.dump(config_data, f, indent=4, ensure_ascii=False)
|
||||
|
||||
# ================= 1. 核心逻辑功能 =================
|
||||
|
||||
def get_llm_models(api_key, base_url):
|
||||
if not api_key or not base_url:
|
||||
return gr.update(choices=[]), "⚠️ 请先填写配置"
|
||||
try:
|
||||
url = f"{base_url.rstrip('/')}/models"
|
||||
headers = {"Authorization": f"Bearer {api_key}"}
|
||||
response = requests.get(url, headers=headers, timeout=10)
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
models = [item['id'] for item in data.get('data', [])]
|
||||
|
||||
# 保存配置
|
||||
cfg = ConfigManager.load()
|
||||
cfg['api_key'] = api_key
|
||||
cfg['base_url'] = base_url
|
||||
ConfigManager.save(cfg)
|
||||
|
||||
# 修复警告:允许自定义值
|
||||
return gr.update(choices=models, value=models[0] if models else None), f"✅ 已连接,加载 {len(models)} 个模型"
|
||||
return gr.update(), f"❌ 连接失败: {response.status_code}"
|
||||
except Exception as e:
|
||||
return gr.update(), f"❌ 错误: {e}"
|
||||
|
||||
def generate_copy(api_key, base_url, model, topic, style):
|
||||
if not api_key: return "", "", "", "❌ 缺 API Key"
|
||||
|
||||
# --- 核心修改:优化了 Prompt,增加字数和违禁词限制 ---
|
||||
system_prompt = """
|
||||
你是一个小红书爆款内容专家。请根据用户主题生成内容。
|
||||
|
||||
【标题规则】(严格执行):
|
||||
1. 长度限制:必须控制在 18 字以内(含Emoji),绝对不能超过 20 字!
|
||||
2. 格式要求:Emoji + 爆点关键词 + 核心痛点。
|
||||
3. 禁忌:禁止使用“第一”、“最”、“顶级”等绝对化广告法违禁词。
|
||||
4. 风格:二极管标题(震惊/后悔/必看/避雷/哭了),具有强烈的点击欲望。
|
||||
|
||||
【正文规则】:
|
||||
1. 口语化,多用Emoji,分段清晰,不堆砌长句。
|
||||
2. 结尾必须有 5 个以上相关话题标签(#)。
|
||||
|
||||
【绘图 Prompt】:
|
||||
生成对应的 Stable Diffusion 英文提示词,强调:masterpiece, best quality, 8k, soft lighting, ins style。
|
||||
|
||||
返回 JSON 格式:
|
||||
{"title": "...", "content": "...", "sd_prompt": "..."}
|
||||
"""
|
||||
|
||||
try:
|
||||
headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}
|
||||
payload = {
|
||||
"model": model,
|
||||
"messages": [
|
||||
{"role": "system", "content": system_prompt},
|
||||
{"role": "user", "content": f"主题:{topic}\n风格:{style}"}
|
||||
],
|
||||
"response_format": {"type": "json_object"}
|
||||
}
|
||||
resp = requests.post(f"{base_url.rstrip('/')}/chat/completions", headers=headers, json=payload, timeout=60)
|
||||
|
||||
content = resp.json()['choices'][0]['message']['content']
|
||||
content = re.sub(r'```json\s*|```', '', content).strip()
|
||||
data = json.loads(content)
|
||||
|
||||
# --- 双重保险:Python 强制截断 ---
|
||||
title = data.get('title', '')
|
||||
# 如果 LLM 不听话超过了20字,强制截断并保留前19个字+省略号,或者直接保留前20个
|
||||
if len(title) > 20:
|
||||
title = title[:20]
|
||||
|
||||
return title, data.get('content', ''), data.get('sd_prompt', ''), "✅ 文案生成完毕"
|
||||
except Exception as e:
|
||||
return "", "", "", f"❌ 生成失败: {e}"
|
||||
|
||||
def get_sd_models(sd_url):
|
||||
try:
|
||||
resp = requests.get(f"{sd_url}/sdapi/v1/sd-models", timeout=3)
|
||||
if resp.status_code == 200:
|
||||
models = [m['title'] for m in resp.json()]
|
||||
return gr.update(choices=models, value=models[0] if models else None), "✅ SD 已连接"
|
||||
return gr.update(choices=[]), "❌ SD 连接失败"
|
||||
except:
|
||||
return gr.update(choices=[]), "❌ SD 未启动或端口错误"
|
||||
|
||||
def generate_images(sd_url, prompt, neg_prompt, model, steps, cfg):
|
||||
if not model: return None, "❌ 未选择模型"
|
||||
|
||||
# 切换模型
|
||||
try:
|
||||
requests.post(f"{sd_url}/sdapi/v1/options", json={"sd_model_checkpoint": model})
|
||||
except:
|
||||
pass # 忽略切换错误,继续尝试生成
|
||||
|
||||
payload = {
|
||||
"prompt": prompt,
|
||||
"negative_prompt": neg_prompt,
|
||||
"steps": steps,
|
||||
"cfg_scale": cfg,
|
||||
"width": 768,
|
||||
"height": 1024,
|
||||
"batch_size": 2
|
||||
}
|
||||
|
||||
try:
|
||||
resp = requests.post(f"{sd_url}/sdapi/v1/txt2img", json=payload, timeout=120)
|
||||
images = []
|
||||
for i in resp.json()['images']:
|
||||
img = Image.open(io.BytesIO(base64.b64decode(i)))
|
||||
images.append(img)
|
||||
return images, "✅ 图片生成完毕"
|
||||
except Exception as e:
|
||||
return None, f"❌ 绘图失败: {e}"
|
||||
|
||||
def one_click_export(title, content, images):
|
||||
if not title: return "❌ 无法导出:没有标题"
|
||||
|
||||
safe_title = re.sub(r'[\\/*?:"<>|]', "", title)[:20]
|
||||
folder_name = f"{int(time.time())}_{safe_title}"
|
||||
folder_path = os.path.join(OUTPUT_DIR, folder_name)
|
||||
os.makedirs(folder_path, exist_ok=True)
|
||||
|
||||
with open(os.path.join(folder_path, "文案.txt"), "w", encoding="utf-8") as f:
|
||||
f.write(f"{title}\n\n{content}")
|
||||
|
||||
if images:
|
||||
for idx, img in enumerate(images):
|
||||
img.save(os.path.join(folder_path, f"图{idx+1}.png"))
|
||||
|
||||
try:
|
||||
if platform.system() == "Windows":
|
||||
os.startfile(folder_path)
|
||||
elif platform.system() == "Darwin":
|
||||
subprocess.call(["open", folder_path])
|
||||
else:
|
||||
subprocess.call(["xdg-open", folder_path])
|
||||
return f"✅ 已导出至: {folder_path}"
|
||||
except:
|
||||
return f"✅ 已导出: {folder_path}"
|
||||
|
||||
# ================= 2. UI 界面构建 =================
|
||||
|
||||
cfg = ConfigManager.load()
|
||||
|
||||
with gr.Blocks(title="小红书全自动工作台", theme=gr.themes.Soft()) as app:
|
||||
gr.Markdown("## 🍒 小红书 AI 爆文生产工坊")
|
||||
|
||||
state_images = gr.State([])
|
||||
|
||||
with gr.Row():
|
||||
with gr.Column(scale=1):
|
||||
with gr.Accordion("⚙️ 系统设置 (自动保存)", open=True):
|
||||
api_key = gr.Textbox(label="LLM API Key", value=cfg['api_key'], type="password")
|
||||
base_url = gr.Textbox(label="Base URL", value=cfg['base_url'])
|
||||
sd_url = gr.Textbox(label="SD URL", value=cfg['sd_url'])
|
||||
|
||||
with gr.Row():
|
||||
btn_connect = gr.Button("🔗 连接并获取模型", size="sm")
|
||||
btn_refresh_sd = gr.Button("🔄 刷新 SD", size="sm")
|
||||
|
||||
# 修复点 1:允许自定义值,防止报错
|
||||
llm_model = gr.Dropdown(label="选择 LLM 模型", value=cfg['model'], allow_custom_value=True, interactive=True)
|
||||
sd_model = gr.Dropdown(label="选择 SD 模型", allow_custom_value=True, interactive=True)
|
||||
status_bar = gr.Markdown("等待就绪...")
|
||||
|
||||
gr.Markdown("### 💡 内容构思")
|
||||
topic = gr.Textbox(label="笔记主题", placeholder="例如:优衣库早春穿搭")
|
||||
style = gr.Dropdown(["好物种草", "干货教程", "情绪共鸣", "生活Vlog"], label="风格", value="好物种草")
|
||||
btn_step1 = gr.Button("✨ 第一步:生成文案方案", variant="primary")
|
||||
|
||||
with gr.Column(scale=1):
|
||||
gr.Markdown("### 📝 文案确认")
|
||||
# 修复点 2:去掉了 show_copy_button 参数,兼容旧版 Gradio
|
||||
res_title = gr.Textbox(label="标题 (AI生成)", interactive=True)
|
||||
res_content = gr.TextArea(label="正文 (AI生成)", lines=10, interactive=True)
|
||||
res_prompt = gr.TextArea(label="绘图提示词", lines=4, interactive=True)
|
||||
|
||||
with gr.Accordion("🎨 绘图参数", open=False):
|
||||
neg_prompt = gr.Textbox(label="反向词", value="nsfw, lowres, bad anatomy, text, error")
|
||||
steps = gr.Slider(15, 50, value=25, label="步数")
|
||||
cfg_scale = gr.Slider(1, 15, value=7, label="相关性 (CFG)")
|
||||
|
||||
btn_step2 = gr.Button("🎨 第二步:开始绘图", variant="primary")
|
||||
|
||||
with gr.Column(scale=1):
|
||||
gr.Markdown("### 🖼️ 视觉结果")
|
||||
gallery = gr.Gallery(label="生成预览", columns=1, height="auto")
|
||||
btn_export = gr.Button("📂 一键导出 (文案+图片)", variant="stop")
|
||||
export_msg = gr.Markdown("")
|
||||
|
||||
# ================= 3. 事件绑定 =================
|
||||
|
||||
btn_connect.click(fn=get_llm_models, inputs=[api_key, base_url], outputs=[llm_model, status_bar])
|
||||
btn_refresh_sd.click(fn=get_sd_models, inputs=[sd_url], outputs=[sd_model, status_bar])
|
||||
|
||||
btn_step1.click(
|
||||
fn=generate_copy,
|
||||
inputs=[api_key, base_url, llm_model, topic, style],
|
||||
outputs=[res_title, res_content, res_prompt, status_bar]
|
||||
)
|
||||
|
||||
def on_img_gen(sd_url, p, np, m, s, c):
|
||||
imgs, msg = generate_images(sd_url, p, np, m, s, c)
|
||||
return imgs, imgs, msg
|
||||
|
||||
btn_step2.click(
|
||||
fn=on_img_gen,
|
||||
inputs=[sd_url, res_prompt, neg_prompt, sd_model, steps, cfg_scale],
|
||||
outputs=[gallery, state_images, status_bar]
|
||||
)
|
||||
|
||||
btn_export.click(
|
||||
fn=one_click_export,
|
||||
inputs=[res_title, res_content, state_images],
|
||||
outputs=[export_msg]
|
||||
)
|
||||
|
||||
app.load(fn=get_sd_models, inputs=[sd_url], outputs=[sd_model, status_bar])
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.launch(inbrowser=True)
|
||||
BIN
my_face.png
BIN
my_face.png
Binary file not shown.
|
Before Width: | Height: | Size: 5.4 MiB |
BIN
myself.jpg
BIN
myself.jpg
Binary file not shown.
|
Before Width: | Height: | Size: 99 KiB |
@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-02-26
|
||||
@ -0,0 +1,36 @@
|
||||
## Context
|
||||
|
||||
项目工作区根目录(`f:\3_Personal\AI\xhs_bot\autobot\`)随开发过程积累了多个无用文件:旧版备份(`main_v1_backup.py`)、配置副本(`config copy.json`)、一次性测试脚本(`_test_config_save.py`)、临时记录(`Todo.md`)、运行日志(`autobot.log``)以及散落的个人图片(4 张 `.png`/`.jpg`)。
|
||||
|
||||
这些文件虽已被 `.gitignore` 排除出版本控制,但依然存在于工作区,干扰了目录可读性。无需改动任何运行时逻辑。
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- 删除 5 个明确无用的文件
|
||||
- 将 4 张个人图片归入 `assets/faces/` 目录并更新 `.gitignore`
|
||||
- 完善 `.gitignore` 注释与规则覆盖
|
||||
- 建立 `project-structure` 规范 spec,为后续维护提供参考
|
||||
|
||||
**Non-Goals:**
|
||||
- 不将根目录服务文件(`analytics_service.py`、`llm_service.py` 等)迁移至 `services/`(涉及 import 改动,留作独立变更)
|
||||
- 不修改任何 Python 业务代码
|
||||
- 不调整 Docker / CI 配置
|
||||
|
||||
## Decisions
|
||||
|
||||
**决策 1:图片归档至 `assets/faces/`**
|
||||
- 选择 `assets/faces/` 而非直接删除,因为换脸功能可能在 UI 中需要默认头像路径,保留文件可避免破坏演示/教学场景
|
||||
- 替代方案:直接删除 → 风险是用户文档引用了这些文件路径
|
||||
|
||||
**决策 2:`assets/faces/` 整体纳入 `.gitignore`**
|
||||
- 个人图片属于隐私敏感数据,不应进入版本控制
|
||||
- 在 `.gitignore` 中新增 `assets/faces/` 规则,并补充注释
|
||||
|
||||
**决策 3:`Todo.md` 直接删除而非迁移**
|
||||
- 内容已过时且已有 openspec 任务管理体系替代,迁移意义不大
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- **[风险] `my_face.png` 等文件路径可能被 `config.json` 引用** → 缓解:删除前检查 `config.json` 中是否存在相关路径字段;若存在则仅移动不删除,并在 `config.example.json` 中更新说明
|
||||
- **[风险] `_autostart.bat` / `_autostart.vbs` 与 `assets/faces/` 路径无关,但同属根目录非标准文件** → 此次保留,自动启动脚本属于有效使用场景
|
||||
@ -0,0 +1,33 @@
|
||||
## Why
|
||||
|
||||
项目根目录混入备份文件、临时测试脚本、个人图片等无关文件,导致目录结构混乱、难以辨别哪些是正式代码。部分内容(人脸图片、日志文件)已被 `.gitignore` 排除但仍残留在工作区,须手动清理。
|
||||
|
||||
## What Changes
|
||||
|
||||
- **删除冗余/备份文件**:
|
||||
- `main_v1_backup.py`(旧版备份,已被 `.gitignore` 的 `*_backup.py` 规则覆盖)
|
||||
- `config copy.json`(配置副本,已被 `.gitignore` 覆盖)
|
||||
- `_test_config_save.py`(一次性测试脚本,无保留价值)
|
||||
- `Todo.md`(个人临时记录,已有 openspec 任务管理替代)
|
||||
- `autobot.log`(运行日志,已被 `*.log` 规则覆盖)
|
||||
|
||||
- **整理个人图片**:将散落在根目录的人脸/头像图片(`beauty.png`、`myself.jpg`、`my_face.png`、`zjz.png`)移入 `assets/faces/` 目录,更新 `.gitignore` 将该目录纳入忽略范围
|
||||
|
||||
- **评估根目录服务文件**:检查 `analytics_service.py`、`llm_service.py`、`sd_service.py`、`mcp_client.py`、`publish_queue.py`、`config_manager.py` 是否应迁移至 `services/`;若涉及大量 import 改动则列为独立后续变更,本次仅做评估记录
|
||||
|
||||
- **补全 `.gitignore`**:确保 `assets/faces/`、`*.log`、`__pycache__/` 等规则完整且注释清晰
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
- `project-structure`: 定义项目标准目录结构、根目录文件清单规范及 `.gitignore` 策略
|
||||
|
||||
### Modified Capabilities
|
||||
(无需修改现有 spec)
|
||||
|
||||
## Impact
|
||||
|
||||
- 删除 5 个文件,不影响任何运行时逻辑
|
||||
- `assets/faces/` 目录新增后,换脸功能的默认头像路径可能需要在 `config.example.json` 中更新说明
|
||||
- 对 Docker 构建无影响(`.dockerignore` 已存在)
|
||||
- 无 API 变更,无依赖变更
|
||||
@ -0,0 +1,49 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 根目录只包含正式项目文件
|
||||
项目根目录 SHALL 不包含备份文件、一次性测试脚本、个人媒体资源或临时记录文件。具体地:
|
||||
|
||||
- `*_backup.py` 命名的文件 SHALL 不存在于根目录
|
||||
- `config copy.json`(或同类配置副本)SHALL 不存在
|
||||
- `_test_*.py` 命名的一次性测试脚本 SHALL 不存在于根目录
|
||||
- `*.log` 运行日志文件 SHALL 不进入版本控制(由 `.gitignore` 保证)
|
||||
- 个人图片(`.png`、`.jpg` 等媒体文件)SHALL 不散落在根目录,统一归入 `assets/` 下对应子目录
|
||||
|
||||
#### Scenario: 克隆仓库后根目录无冗余文件
|
||||
- **WHEN** 开发者执行 `git clone` 并查看根目录
|
||||
- **THEN** 根目录 SHALL 仅包含:`main.py`、服务模块文件(`*_service.py`、`*_client.py`、`*_manager.py`、`*_queue.py`)、标准配置(`config.example.json`、`requirements.txt`、`Dockerfile`、`docker-compose.yml`、`.gitignore`、`.dockerignore`)、自动启动脚本(`_autostart.*`)、文档(`README.md`、`CHANGELOG.md`、`CONTRIBUTING.md`、`mcp.md`)
|
||||
|
||||
#### Scenario: 备份文件不出现在版本控制
|
||||
- **WHEN** 开发者执行 `git status` 或 `git ls-files`
|
||||
- **THEN** 输出中 SHALL 不包含 `*_backup.py`、`config copy.json` 等备份/副本文件
|
||||
|
||||
### Requirement: 媒体资源归入 assets/ 目录
|
||||
项目所需的图片等媒体资源 SHALL 存放于 `assets/` 目录下的对应子目录,根据用途分类:
|
||||
|
||||
- 换脸/头像相关图片 SHALL 放入 `assets/faces/`
|
||||
- `assets/faces/` SHALL 被 `.gitignore` 覆盖(不进入版本控制,属隐私数据)
|
||||
|
||||
#### Scenario: 换脸图片存放路径符合规范
|
||||
- **WHEN** 用户配置换脸头像功能
|
||||
- **THEN** 头像文件 SHALL 存放于 `assets/faces/` 目录,而非项目根目录
|
||||
|
||||
#### Scenario: assets/faces/ 不进入版本控制
|
||||
- **WHEN** 开发者执行 `git status`
|
||||
- **THEN** `assets/faces/` 目录下的文件 SHALL 显示为已忽略(不出现在 staged 或 unstaged 区域)
|
||||
|
||||
### Requirement: .gitignore 覆盖所有非版本控制内容
|
||||
项目 `.gitignore` SHALL 包含以下规则类别,且每类规则 SHALL 有注释说明用途:
|
||||
|
||||
- Python 编译产物(`__pycache__/`、`*.py[cod]`)
|
||||
- 虚拟环境目录(`.venv/`、`venv/`)
|
||||
- 敏感配置(`config.json`、`cookies.json`、`*.cookie`)
|
||||
- 运行日志(`*.log`、`logs/`)
|
||||
- 备份与副本文件(`*_backup.py`、`config copy.json`)
|
||||
- 个人媒体资产(`assets/faces/`)
|
||||
- IDE 配置(`.vscode/`、`.idea/`)
|
||||
- 系统文件(`.DS_Store`、`Thumbs.db`)
|
||||
- 工作空间输出目录(`xhs_workspace/`)
|
||||
|
||||
#### Scenario: 新增备份文件不被 git 追踪
|
||||
- **WHEN** 开发者在根目录创建 `main_v2_backup.py`
|
||||
- **THEN** `git status` SHALL 将其显示为已忽略文件
|
||||
@ -0,0 +1,25 @@
|
||||
## 1. 删除冗余文件
|
||||
|
||||
- [x] 1.1 删除 `main_v1_backup.py`(旧版备份,无保留价值)
|
||||
- [x] 1.2 删除 `config copy.json`(配置副本,无保留价值)
|
||||
- [x] 1.3 删除 `_test_config_save.py`(一次性测试脚本)
|
||||
- [x] 1.4 删除 `Todo.md`(临时记录,已由 openspec 替代)
|
||||
- [x] 1.5 删除 `autobot.log`(运行日志,已被 `.gitignore` 覆盖)
|
||||
|
||||
## 2. 整理图片资源
|
||||
|
||||
- [x] 2.1 创建 `assets/faces/` 目录
|
||||
- [x] 2.2 将 `my_face.png` 移入 `assets/faces/my_face.png`
|
||||
- [x] 2.3 将 `beauty.png`、`myself.jpg`、`zjz.png` 移入 `assets/faces/`
|
||||
- [x] 2.4 更新 `sd_service.py` 第 22 行的 `FACE_IMAGE_PATH`,将路径从 `"my_face.png"` 改为 `os.path.join(os.path.dirname(__file__), "assets", "faces", "my_face.png")`
|
||||
|
||||
## 3. 完善 .gitignore
|
||||
|
||||
- [x] 3.1 在 `.gitignore` 中新增 `assets/faces/` 规则,并加注释(个人头像不入版本控制)
|
||||
- [x] 3.2 确认 `.gitignore` 中 `*.log` 规则已存在(已有,检查确认即可)
|
||||
|
||||
## 4. 回归验证
|
||||
|
||||
- [ ] 4.1 启动应用 `python main.py`,确认换脸功能(⚙️ 配置 Tab AI 换脸头像)加载正常
|
||||
- [ ] 4.2 确认 `SDService.load_face_image()` 能正确读取新路径下的 `my_face.png`
|
||||
- [x] 4.3 执行 `git status` 确认 `assets/faces/` 已被忽略,根目录无残留冗余文件
|
||||
@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-02-27
|
||||
@ -0,0 +1,55 @@
|
||||
## Context
|
||||
|
||||
当前项目根目录同时存在 6 个业务服务文件(`config_manager.py`、`llm_service.py`、`sd_service.py`、`mcp_client.py`、`analytics_service.py`、`publish_queue.py`)与 `services/` 包目录并列,架构层次混乱。`services/` 下游代码(`scheduler.py` 等)通过裸名导入(flat import)引用这些文件,依赖 Python 将根目录隐式加入 `sys.path` 这一运行时特性。
|
||||
|
||||
迁移约束:
|
||||
- `services/__init__.py` 已存在,`services/` 是合法 Python 包
|
||||
- 所有受影响文件共约 20 处 import,分布在 `main.py`、`ui/app.py`、`ui/tab_create.py`、`services/*.py`
|
||||
- 必须保证 Python 不产生循环导入(circular import)
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- 将 6 个服务文件移入 `services/`,根目录只保留 `main.py` 一个 Python 文件
|
||||
- 所有 import 使用绝对路径(`from services.xxx import ...`),保持一致性、可读性
|
||||
- `services/` 内部文件之间使用相对导入(`from .xxx import ...`),减少路径依赖
|
||||
- 不引入任何新的运行时依赖或行为变更
|
||||
|
||||
**Non-Goals:**
|
||||
- 不拆分或合并任何模块的内部逻辑
|
||||
- 不更改 `services/__init__.py` 的公开导出(除非需要兼容性垫片)
|
||||
- 不迁移 `ui/` 下的文件(已有独立模块结构)
|
||||
|
||||
## Decisions
|
||||
|
||||
**决策 1:外部文件用绝对导入 `from services.xxx import ...`**
|
||||
- `main.py`、`ui/app.py`、`ui/tab_create.py` 均从根目录运行,绝对导入路径清晰、错误信息可读
|
||||
- 替代:在 `services/__init__.py` 重新导出所有符号(向后兼容)→ 增加维护负担,拒绝
|
||||
|
||||
**决策 2:`services/` 内文件之间用相对导入 `from .xxx import ...`**
|
||||
- 避免 `services/` 内部对根目录的隐式依赖,打包或测试时不受 `sys.path` 影响
|
||||
- 替代:也用绝对导入 → 可行但不如相对导入内聚
|
||||
|
||||
**决策 3:分两阶段迁移(先移文件,再改 import)**
|
||||
- 防止中间状态同时修改多文件导致错误难以定位
|
||||
- 迁移顺序:`config_manager` → `mcp_client` → `llm_service` → `sd_service` → `analytics_service` → `publish_queue`(按依赖深度从浅到深)
|
||||
|
||||
**决策 4:不添加兼容性垫片(shim)**
|
||||
- 项目无外部 PyPI 消费者,无向后兼容压力,直接修改所有 import 更干净
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- **[风险] `services/` 内循环导入** → 缓解:迁移前用 `grep` 确认依赖关系,`config_manager` 通常是叶节点(最少依赖),优先迁移
|
||||
- **[风险] `ui/app.py` 在迁移中途启动失败** → 缓解:一次性修改所有文件 import,不留半迁移状态;迁移后立即用 `ast.parse()` 验证语法
|
||||
- **[风险] Dockerfile `COPY . .` 已是整目录复制** → 无影响,`services/` 子目录正常复制
|
||||
- **[风险] CI 首次运行可能因 GitHub Secrets 缺失(如无 `GITHUB_TOKEN`)报错** → 缓解:CI 仅做静态检查,不需要 Secrets
|
||||
|
||||
## Migration Plan
|
||||
|
||||
1. 将 6 个文件 `Move-Item` 到 `services/`
|
||||
2. 批量替换所有外部文件(`main.py`、`ui/*.py`)中的 import:`from xxx` → `from services.xxx`
|
||||
3. 批量替换 `services/*.py` 中的内部 import:`from xxx` → `from .xxx`
|
||||
4. 语法验证:对所有修改文件执行 `ast.parse()`
|
||||
5. 启动验证:`python -c "from ui.app import build_app"` 确认无导入错误
|
||||
|
||||
**回滚方案:** 删除 `services/` 下新迁移的文件,将 git 恢复(或手动移回根目录),改回 import)
|
||||
@ -0,0 +1,57 @@
|
||||
## Why
|
||||
|
||||
项目代码已具备完整功能,但存在两个问题:①缺少优秀开源项目的标准配置(Issue/PR 模板、CI、Code of Conduct、Security Policy),降低社区协作门槛和可信度;②根目录混杂 6 个服务文件(`llm_service.py`、`sd_service.py`、`mcp_client.py`、`analytics_service.py`、`publish_queue.py`、`config_manager.py`),与 `services/` 模块目录并列,架构层次不清晰。两个问题结合目录清理后的稳定状态一并解决。
|
||||
|
||||
## What Changes
|
||||
|
||||
### 目录结构整理
|
||||
- **将 6 个根目录服务文件迁移至 `services/`**:
|
||||
- `config_manager.py` → `services/config_manager.py`
|
||||
- `llm_service.py` → `services/llm_service.py`
|
||||
- `sd_service.py` → `services/sd_service.py`
|
||||
- `mcp_client.py` → `services/mcp_client.py`
|
||||
- `analytics_service.py` → `services/analytics_service.py`
|
||||
- `publish_queue.py` → `services/publish_queue.py`
|
||||
- **更新所有受影响文件的 import 语句**(`main.py`、`ui/app.py`、`ui/tab_create.py`、`services/*.py` 共约 20 处)
|
||||
- **更新 `services/__init__.py`** 导出新增模块(可选,保持向后兼容)
|
||||
|
||||
目标根目录结构:
|
||||
```
|
||||
autobot/
|
||||
├── main.py # 程序入口(唯一根目录 .py)
|
||||
├── ui/ # UI 层 (Gradio)
|
||||
├── services/ # 全部业务逻辑层(迁移后)
|
||||
├── assets/ # 静态资源
|
||||
├── config.example.json # 配置模板
|
||||
├── requirements.txt # 生产依赖
|
||||
├── requirements-dev.txt # 开发依赖(新增)
|
||||
├── Dockerfile / docker-compose.yml
|
||||
└── *.md / LICENSE # 文档
|
||||
```
|
||||
|
||||
### 开源社区标准文件
|
||||
- **新增 GitHub Issue 模板** — `.github/ISSUE_TEMPLATE/bug_report.md` 和 `feature_request.md`
|
||||
- **新增 PR 模板** — `.github/pull_request_template.md`
|
||||
- **新增 CI 工作流** — `.github/workflows/ci.yml`(Push/PR 触发 ruff + 导入验证)
|
||||
- **新增 Code of Conduct** — `CODE_OF_CONDUCT.md`(Contributor Covenant v2.1 中文版)
|
||||
- **新增 Security Policy** — `SECURITY.md`
|
||||
- **新增 `requirements-dev.txt`** — ruff、pre-commit
|
||||
- **完善 README**:顶部徽章(Python、License、CI)、修正项目结构图、替换 `your-username` 占位符
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
- `project-restructure`: 根目录服务文件迁移至 `services/`,全量 import 更新,分层架构清晰
|
||||
- `oss-community-health`: Issue 模板、PR 模板、Code of Conduct、Security Policy
|
||||
- `oss-ci-workflow`: GitHub Actions CI(ruff 代码检查 + 导入验证)
|
||||
- `oss-readme-polish`: README 徽章、结构说明修正、占位符修复
|
||||
|
||||
### Modified Capabilities
|
||||
(无需修改现有 spec)
|
||||
|
||||
## Impact
|
||||
|
||||
- `project-restructure`:影响 `main.py`、`ui/app.py`、`ui/tab_create.py`、`services/` 下全部文件(仅修改 import 路径,不改业务逻辑)
|
||||
- 社区文件和 CI 仅新增,不修改现有代码
|
||||
- Dockerfile 的 `COPY` 指令兼容(整目录复制,无需修改)
|
||||
- 对运行时行为、Docker 部署、依赖均无影响
|
||||
@ -0,0 +1,37 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: GitHub Actions CI 工作流在 Push 和 PR 时自动触发
|
||||
项目 SHALL 在 `.github/workflows/ci.yml` 提供持续集成工作流,在每次 Push 到 `main` 分支或创建 Pull Request 时自动执行代码质量检查。
|
||||
|
||||
#### Scenario: CI 在 PR 时自动运行
|
||||
- **WHEN** 贡献者向 `main` 分支提交 Pull Request
|
||||
- **THEN** GitHub Actions SHALL 自动触发 CI 工作流,在 PR 页面显示检查结果
|
||||
|
||||
#### Scenario: CI 通过后状态徽章更新
|
||||
- **WHEN** CI 工作流执行完毕
|
||||
- **THEN** 工作流状态 SHALL 可通过 Badge URL 获取,用于 README 展示
|
||||
|
||||
### Requirement: CI 执行 ruff 代码风格检查
|
||||
CI 工作流 SHALL 使用 `ruff` 对所有 Python 文件执行代码风格和常见错误检查,ruff 配置 SHALL 允许项目现有代码通过(宽松规则集)。
|
||||
|
||||
#### Scenario: 代码风格检查通过
|
||||
- **WHEN** CI 工作流中的 ruff 步骤执行
|
||||
- **THEN** 对 `*.py` 文件的 ruff 检查 SHALL 以 exit code 0 退出,工作流标记为 passed
|
||||
|
||||
#### Scenario: 引入明显错误时 CI 失败
|
||||
- **WHEN** PR 中包含未使用的 import 或明显语法问题
|
||||
- **THEN** ruff SHALL 检测到并返回非零 exit code,导致 CI 失败
|
||||
|
||||
### Requirement: CI 执行导入验证
|
||||
CI 工作流 SHALL 执行 Python 导入验证步骤,确认核心模块可被正常导入,及早发现迁移引入的导入错误。
|
||||
|
||||
#### Scenario: 导入验证通过
|
||||
- **WHEN** CI 执行导入验证步骤
|
||||
- **THEN** `python -c "from services.config_manager import ConfigManager"` 等关键导入 SHALL 成功执行
|
||||
|
||||
### Requirement: 提供 requirements-dev.txt 开发依赖声明
|
||||
项目根目录 SHALL 包含 `requirements-dev.txt`,声明开发和 CI 所需依赖(ruff、pre-commit 等),与生产依赖 `requirements.txt` 分离。
|
||||
|
||||
#### Scenario: 安装开发依赖命令可正常执行
|
||||
- **WHEN** 开发者执行 `pip install -r requirements-dev.txt`
|
||||
- **THEN** 所有开发工具 SHALL 成功安装,无版本冲突
|
||||
@ -0,0 +1,33 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 提供标准化 GitHub Issue 模板
|
||||
项目 SHALL 在 `.github/ISSUE_TEMPLATE/` 下提供至少两个 Issue 模板:Bug 报告模板和功能请求模板,引导贡献者提供必要信息。
|
||||
|
||||
#### Scenario: Bug 报告模板包含必要字段
|
||||
- **WHEN** 用户在 GitHub 上新建 Issue 并选择「Bug 报告」
|
||||
- **THEN** 模板 SHALL 包含:问题描述、复现步骤、预期行为、实际行为、环境信息(Python 版本、操作系统)
|
||||
|
||||
#### Scenario: 功能请求模板包含场景描述
|
||||
- **WHEN** 用户选择「功能请求」模板
|
||||
- **THEN** 模板 SHALL 包含:问题/需求背景、期望的解决方案、替代方案考虑
|
||||
|
||||
### Requirement: 提供 Pull Request 模板
|
||||
项目 SHALL 在 `.github/pull_request_template.md` 提供 PR 模板,引导贡献者说明变更范围和测试情况。
|
||||
|
||||
#### Scenario: PR 模板包含变更说明和测试确认
|
||||
- **WHEN** 贡献者在 GitHub 上发起 Pull Request
|
||||
- **THEN** 模板 SHALL 包含:变更类型(Bug Fix / Feature / Docs / Refactor)、变更描述、测试说明、相关 Issue 引用
|
||||
|
||||
### Requirement: 包含行为准则文件
|
||||
项目根目录 SHALL 包含 `CODE_OF_CONDUCT.md`,采用 Contributor Covenant v2.1 中文版,明确社区行为规范和违规处理方式。
|
||||
|
||||
#### Scenario: 行为准则文件可访问
|
||||
- **WHEN** 贡献者查看项目根目录
|
||||
- **THEN** `CODE_OF_CONDUCT.md` SHALL 存在,包含社区行为规范、适用范围、执行说明、联系方式
|
||||
|
||||
### Requirement: 包含安全漏洞报告政策
|
||||
项目根目录 SHALL 包含 `SECURITY.md`,说明如何负责任地披露安全漏洞、支持的版本范围和响应时间承诺。
|
||||
|
||||
#### Scenario: 安全政策文件包含报告方式
|
||||
- **WHEN** 安全研究者发现漏洞
|
||||
- **THEN** `SECURITY.md` SHALL 提供私下联系方式(邮件或 GitHub Security Advisory),不要求通过公开 Issue 上报
|
||||
@ -0,0 +1,29 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: README 顶部展示状态徽章
|
||||
`README.md` 顶部(标题下方)SHALL 包含至少三枚徽章:Python 版本要求、License 类型、CI 状态,采用 shields.io 或 GitHub Actions 徽章格式。
|
||||
|
||||
#### Scenario: 徽章在 GitHub 页面正常渲染
|
||||
- **WHEN** 访问项目 GitHub 主页
|
||||
- **THEN** README 顶部 SHALL 显示可点击的 Python、MIT License、CI 状态徽章,链接指向对应资源
|
||||
|
||||
### Requirement: README 项目结构图反映实际代码
|
||||
`README.md` 中的「项目结构」章节 SHALL 反映迁移后的实际目录结构,包含 `services/`(含所有迁移后文件)和 `ui/`(含 `app.py`、`tab_create.py`)的正确层级。
|
||||
|
||||
#### Scenario: 项目结构与 ls 输出一致
|
||||
- **WHEN** 开发者对照 README 查看实际文件目录
|
||||
- **THEN** README 的结构图 SHALL 与实际 `Get-ChildItem` / `ls` 输出一致,无过时文件或缺失目录
|
||||
|
||||
### Requirement: README 不包含 your-username 占位符
|
||||
`README.md` 中所有 `your-username` 占位符 SHALL 替换为实际仓库路径说明或格式示例,使克隆/安装命令可直接复制使用。
|
||||
|
||||
#### Scenario: 安装命令无需手动替换占位符
|
||||
- **WHEN** 用户复制 README 中的 `git clone` 命令
|
||||
- **THEN** 命令 SHALL 包含实际仓库 URL 或明确的 `<your-github-username>` 格式提示,不出现 `your-username` 字符串
|
||||
|
||||
### Requirement: README 使用指南与当前 UI 结构匹配
|
||||
`README.md` 中的「使用指南」和「首次使用流程」章节 SHALL 引用当前正确的 Tab 名称和操作路径,与 `ui/app.py` 实际 Tab 顺序保持一致(⚙️ 配置 Tab 已迁移,不再是「展开全局设置折叠块」)。
|
||||
|
||||
#### Scenario: 首次使用步骤描述与 UI 一致
|
||||
- **WHEN** 新用户按照 README「首次使用流程」操作
|
||||
- **THEN** README 中描述的 Tab 名称和操作入口 SHALL 与实际 Gradio UI 一致,用户无需猜测
|
||||
@ -0,0 +1,34 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 服务层文件统一归入 services/ 包
|
||||
所有业务服务模块 SHALL 位于 `services/` 目录下,根目录除 `main.py` 外 SHALL 不包含任何 `.py` 业务文件。
|
||||
|
||||
迁移文件清单:
|
||||
- `config_manager.py` → `services/config_manager.py`
|
||||
- `llm_service.py` → `services/llm_service.py`
|
||||
- `sd_service.py` → `services/sd_service.py`
|
||||
- `mcp_client.py` → `services/mcp_client.py`
|
||||
- `analytics_service.py` → `services/analytics_service.py`
|
||||
- `publish_queue.py` → `services/publish_queue.py`
|
||||
|
||||
#### Scenario: 根目录不存在游离服务文件
|
||||
- **WHEN** 开发者查看项目根目录
|
||||
- **THEN** 根目录 SHALL 仅含 `main.py` 作为唯一 Python 入口,其余 `.py` 文件均位于 `ui/` 或 `services/` 子目录
|
||||
|
||||
### Requirement: 外部模块使用绝对导入访问 services/
|
||||
`main.py`、`ui/app.py`、`ui/tab_create.py` 等根目录/UI 层文件在导入服务模块时 SHALL 使用绝对导入格式 `from services.<module> import ...`。
|
||||
|
||||
#### Scenario: main.py 正常启动无 ImportError
|
||||
- **WHEN** 在项目根目录执行 `python main.py`
|
||||
- **THEN** 应用 SHALL 正常启动,不抛出任何 `ImportError` 或 `ModuleNotFoundError`
|
||||
|
||||
#### Scenario: UI 层导入路径正确
|
||||
- **WHEN** 执行 `python -c "import ui.app"`
|
||||
- **THEN** 不抛出导入错误,所有 `from services.*` 引用 SHALL 可正常解析
|
||||
|
||||
### Requirement: services/ 内部使用相对导入
|
||||
`services/` 包内各模块之间的相互引用 SHALL 使用相对导入格式 `from .<module> import ...`,不依赖根目录在 `sys.path` 中的位置。
|
||||
|
||||
#### Scenario: services 内部导入独立于运行上下文
|
||||
- **WHEN** 在任意工作目录执行 `python -m services.scheduler`(或类似模块测试)
|
||||
- **THEN** 内部相对导入 SHALL 正常解析,不因工作目录不同而失败
|
||||
186
openspec/changes/archive/2026-02-27-open-source-ready/tasks.md
Normal file
186
openspec/changes/archive/2026-02-27-open-source-ready/tasks.md
Normal file
@ -0,0 +1,186 @@
|
||||
## Tasks
|
||||
|
||||
### 1. 迁移服务文件至 services/ 包(project-restructure)
|
||||
|
||||
- [x] **1.1** 将 `config_manager.py` 移入 `services/`
|
||||
```powershell
|
||||
Move-Item config_manager.py services\config_manager.py
|
||||
```
|
||||
|
||||
- [x] **1.2** 将 `mcp_client.py` 移入 `services/`
|
||||
```powershell
|
||||
Move-Item mcp_client.py services\mcp_client.py
|
||||
```
|
||||
|
||||
- [x] **1.3** 将 `llm_service.py` 移入 `services/`
|
||||
```powershell
|
||||
Move-Item llm_service.py services\llm_service.py
|
||||
```
|
||||
|
||||
- [x] **1.4** 将 `sd_service.py` 移入 `services/`
|
||||
```powershell
|
||||
Move-Item sd_service.py services\sd_service.py
|
||||
```
|
||||
|
||||
- [x] **1.5** 将 `analytics_service.py` 移入 `services/`
|
||||
```powershell
|
||||
Move-Item analytics_service.py services\analytics_service.py
|
||||
```
|
||||
|
||||
- [x] **1.6** 将 `publish_queue.py` 移入 `services/`
|
||||
```powershell
|
||||
Move-Item publish_queue.py services\publish_queue.py
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. 更新外部文件的绝对导入(main.py、ui/)
|
||||
|
||||
- [x] **2.1** 更新 `main.py` 中的导入
|
||||
- `from config_manager import ConfigManager, OUTPUT_DIR` → `from services.config_manager import ConfigManager, OUTPUT_DIR`
|
||||
- `from llm_service import LLMService` → `from services.llm_service import LLMService`
|
||||
|
||||
- [x] **2.2** 更新 `ui/app.py` 中的导入
|
||||
- `from config_manager import ConfigManager` → `from services.config_manager import ConfigManager`
|
||||
- `from sd_service import SDService, DEFAULT_NEGATIVE, FACE_IMAGE_PATH, ...` → `from services.sd_service import SDService, DEFAULT_NEGATIVE, FACE_IMAGE_PATH, ...`
|
||||
- `from analytics_service import AnalyticsService` → `from services.analytics_service import AnalyticsService`
|
||||
- `from publish_queue import STATUS_LABELS` → `from services.publish_queue import STATUS_LABELS`
|
||||
|
||||
- [x] **2.3** 更新 `ui/tab_create.py` 中的导入(检查并替换所有根目录服务模块引用)
|
||||
|
||||
---
|
||||
|
||||
### 3. 更新 services/ 内部使用相对导入
|
||||
|
||||
- [x] **3.1** 更新 `services/scheduler.py`
|
||||
- `from config_manager import ConfigManager, OUTPUT_DIR` → `from .config_manager import ConfigManager, OUTPUT_DIR`
|
||||
- `from llm_service import LLMService` → `from .llm_service import LLMService`
|
||||
- `from sd_service import SDService` → `from .sd_service import SDService`
|
||||
- `from mcp_client import get_mcp_client` → `from .mcp_client import get_mcp_client`
|
||||
- `from analytics_service import AnalyticsService` → `from .analytics_service import AnalyticsService`
|
||||
|
||||
- [x] **3.2** 更新 `services/content.py`
|
||||
- `from config_manager import ConfigManager, OUTPUT_DIR` → `from .config_manager import ConfigManager, OUTPUT_DIR`
|
||||
- `from llm_service import LLMService` → `from .llm_service import LLMService`
|
||||
- `from sd_service import SDService, get_sd_preset` → `from .sd_service import SDService, get_sd_preset`
|
||||
- `from mcp_client import get_mcp_client` → `from .mcp_client import get_mcp_client`
|
||||
|
||||
- [x] **3.3** 更新 `services/hotspot.py`
|
||||
- `from llm_service import LLMService` → `from .llm_service import LLMService`
|
||||
- `from mcp_client import get_mcp_client` → `from .mcp_client import get_mcp_client`
|
||||
|
||||
- [x] **3.4** 更新 `services/engagement.py`
|
||||
- `from mcp_client import get_mcp_client` → `from .mcp_client import get_mcp_client`
|
||||
- `from llm_service import LLMService` → `from .llm_service import LLMService`
|
||||
|
||||
- [x] **3.5** 更新 `services/profile.py`
|
||||
- `from mcp_client import get_mcp_client` → `from .mcp_client import get_mcp_client`
|
||||
|
||||
- [x] **3.6** 更新 `services/persona.py`
|
||||
- `from config_manager import ConfigManager` → `from .config_manager import ConfigManager`
|
||||
|
||||
- [x] **3.7** 检查 `services/queue_ops.py`、`services/rate_limiter.py`、`services/autostart.py`、`services/connection.py` 有无根目录模块引用,按需更新
|
||||
|
||||
---
|
||||
|
||||
### 4. 回归验证——导入与语法检查
|
||||
|
||||
- [x] **4.1** 对所有修改文件执行 Python 语法验证
|
||||
```powershell
|
||||
python -c "
|
||||
import ast, pathlib
|
||||
files = ['main.py','ui/app.py','ui/tab_create.py',
|
||||
'services/scheduler.py','services/content.py',
|
||||
'services/hotspot.py','services/engagement.py',
|
||||
'services/profile.py','services/persona.py']
|
||||
for f in files:
|
||||
ast.parse(pathlib.Path(f).read_text(encoding='utf-8'))
|
||||
print(f'OK: {f}')
|
||||
"
|
||||
```
|
||||
|
||||
- [x] **4.2** 执行核心服务导入验证
|
||||
```powershell
|
||||
python -c "from services.config_manager import ConfigManager; print('config_manager OK')"
|
||||
python -c "from services.llm_service import LLMService; print('llm_service OK')"
|
||||
python -c "from services.sd_service import SDService; print('sd_service OK')"
|
||||
python -c "from services.mcp_client import get_mcp_client; print('mcp_client OK')"
|
||||
python -c "from services.analytics_service import AnalyticsService; print('analytics_service OK')"
|
||||
python -c "from services.publish_queue import STATUS_LABELS; print('publish_queue OK')"
|
||||
```
|
||||
|
||||
- [x] **4.3** 执行 UI 层导入验证
|
||||
```powershell
|
||||
python -c "import ui.app; print('ui.app OK')"
|
||||
```
|
||||
|
||||
- [x] **4.4** 确认根目录无游离 `.py` 业务文件
|
||||
```powershell
|
||||
Get-ChildItem -Path . -MaxDepth 1 -Filter "*.py" | Select-Object Name
|
||||
# 预期仅显示 main.py(以及测试脚本如 _test_config_save.py)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5. 添加社区健康文件(oss-community-health)
|
||||
|
||||
- [x] **5.1** 创建 `.github/ISSUE_TEMPLATE/bug_report.md`(Bug 报告模板)
|
||||
包含:问题描述、复现步骤、预期行为、实际行为、环境信息(Python 版本、OS)
|
||||
|
||||
- [x] **5.2** 创建 `.github/ISSUE_TEMPLATE/feature_request.md`(功能请求模板)
|
||||
包含:背景/需求、期望解决方案、替代方案
|
||||
|
||||
- [x] **5.3** 创建 `.github/pull_request_template.md`(PR 模板)
|
||||
包含:变更类型(Bug Fix / Feature / Docs / Refactor)、变更描述、测试说明、相关 Issue
|
||||
|
||||
- [x] **5.4** 创建 `CODE_OF_CONDUCT.md`(Contributor Covenant v2.1 中文版)
|
||||
|
||||
- [x] **5.5** 创建 `SECURITY.md`(安全漏洞报告政策)
|
||||
包含:支持版本、私下报告方式(GitHub Security Advisory)、响应时间承诺
|
||||
|
||||
---
|
||||
|
||||
### 6. 添加 CI 工作流(oss-ci-workflow)
|
||||
|
||||
- [x] **6.1** 创建 `requirements-dev.txt`,包含 `ruff>=0.4.0`
|
||||
|
||||
- [x] **6.2** 创建 `.github/workflows/ci.yml`
|
||||
- trigger: `push` to `main`、`pull_request` to `main`
|
||||
- job `lint`:
|
||||
- `pip install ruff`
|
||||
- `ruff check . --select E,F,W --ignore E501`(宽松规则,忽略行长)
|
||||
- job `import-check`:
|
||||
- `pip install -r requirements.txt`
|
||||
- `python -c "from services.config_manager import ConfigManager"`
|
||||
- `python -c "from services.llm_service import LLMService"`
|
||||
- `python -c "from services.sd_service import SDService"`
|
||||
|
||||
---
|
||||
|
||||
### 7. 完善 README(oss-readme-polish)
|
||||
|
||||
- [x] **7.1** 在 README 标题下方添加徽章(Python、MIT License、CI Status)
|
||||
```markdown
|
||||

|
||||
[](LICENSE)
|
||||
[](https://github.com/<your-github-username>/autobot/actions/workflows/ci.yml)
|
||||
```
|
||||
> 将 `<your-github-username>` 替换为实际 GitHub 用户名
|
||||
|
||||
- [x] **7.2** 修正 README 中的「项目结构」章节,反映迁移后 `services/` 的完整内容
|
||||
|
||||
- [x] **7.3** 全局搜索替换 `your-username` 占位符
|
||||
```powershell
|
||||
Select-String -Path README.md -Pattern "your-username"
|
||||
# 确认所有出现位置后,手动或批量替换
|
||||
```
|
||||
|
||||
- [x] **7.4** 检查「首次使用流程」中的 Tab 名称与实际 Gradio UI 一致
|
||||
|
||||
---
|
||||
|
||||
### 8. 最终验证
|
||||
|
||||
- [x] **8.1** 执行 `git status` 确认所有变更文件符合预期
|
||||
- [x] **8.2** 执行 `git diff --stat` 确认无意外文件被修改
|
||||
- [x] **8.3** 启动应用:`python main.py` 确认 Gradio UI 正常加载,无启动错误
|
||||
37
openspec/specs/oss-ci-workflow/spec.md
Normal file
37
openspec/specs/oss-ci-workflow/spec.md
Normal file
@ -0,0 +1,37 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: GitHub Actions CI 工作流在 Push 和 PR 时自动触发
|
||||
项目 SHALL 在 `.github/workflows/ci.yml` 提供持续集成工作流,在每次 Push 到 `main` 分支或创建 Pull Request 时自动执行代码质量检查。
|
||||
|
||||
#### Scenario: CI 在 PR 时自动运行
|
||||
- **WHEN** 贡献者向 `main` 分支提交 Pull Request
|
||||
- **THEN** GitHub Actions SHALL 自动触发 CI 工作流,在 PR 页面显示检查结果
|
||||
|
||||
#### Scenario: CI 通过后状态徽章更新
|
||||
- **WHEN** CI 工作流执行完毕
|
||||
- **THEN** 工作流状态 SHALL 可通过 Badge URL 获取,用于 README 展示
|
||||
|
||||
### Requirement: CI 执行 ruff 代码风格检查
|
||||
CI 工作流 SHALL 使用 `ruff` 对所有 Python 文件执行代码风格和常见错误检查,ruff 配置 SHALL 允许项目现有代码通过(宽松规则集)。
|
||||
|
||||
#### Scenario: 代码风格检查通过
|
||||
- **WHEN** CI 工作流中的 ruff 步骤执行
|
||||
- **THEN** 对 `*.py` 文件的 ruff 检查 SHALL 以 exit code 0 退出,工作流标记为 passed
|
||||
|
||||
#### Scenario: 引入明显错误时 CI 失败
|
||||
- **WHEN** PR 中包含未使用的 import 或明显语法问题
|
||||
- **THEN** ruff SHALL 检测到并返回非零 exit code,导致 CI 失败
|
||||
|
||||
### Requirement: CI 执行导入验证
|
||||
CI 工作流 SHALL 执行 Python 导入验证步骤,确认核心模块可被正常导入,及早发现迁移引入的导入错误。
|
||||
|
||||
#### Scenario: 导入验证通过
|
||||
- **WHEN** CI 执行导入验证步骤
|
||||
- **THEN** `python -c "from services.config_manager import ConfigManager"` 等关键导入 SHALL 成功执行
|
||||
|
||||
### Requirement: 提供 requirements-dev.txt 开发依赖声明
|
||||
项目根目录 SHALL 包含 `requirements-dev.txt`,声明开发和 CI 所需依赖(ruff、pre-commit 等),与生产依赖 `requirements.txt` 分离。
|
||||
|
||||
#### Scenario: 安装开发依赖命令可正常执行
|
||||
- **WHEN** 开发者执行 `pip install -r requirements-dev.txt`
|
||||
- **THEN** 所有开发工具 SHALL 成功安装,无版本冲突
|
||||
33
openspec/specs/oss-community-health/spec.md
Normal file
33
openspec/specs/oss-community-health/spec.md
Normal file
@ -0,0 +1,33 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 提供标准化 GitHub Issue 模板
|
||||
项目 SHALL 在 `.github/ISSUE_TEMPLATE/` 下提供至少两个 Issue 模板:Bug 报告模板和功能请求模板,引导贡献者提供必要信息。
|
||||
|
||||
#### Scenario: Bug 报告模板包含必要字段
|
||||
- **WHEN** 用户在 GitHub 上新建 Issue 并选择「Bug 报告」
|
||||
- **THEN** 模板 SHALL 包含:问题描述、复现步骤、预期行为、实际行为、环境信息(Python 版本、操作系统)
|
||||
|
||||
#### Scenario: 功能请求模板包含场景描述
|
||||
- **WHEN** 用户选择「功能请求」模板
|
||||
- **THEN** 模板 SHALL 包含:问题/需求背景、期望的解决方案、替代方案考虑
|
||||
|
||||
### Requirement: 提供 Pull Request 模板
|
||||
项目 SHALL 在 `.github/pull_request_template.md` 提供 PR 模板,引导贡献者说明变更范围和测试情况。
|
||||
|
||||
#### Scenario: PR 模板包含变更说明和测试确认
|
||||
- **WHEN** 贡献者在 GitHub 上发起 Pull Request
|
||||
- **THEN** 模板 SHALL 包含:变更类型(Bug Fix / Feature / Docs / Refactor)、变更描述、测试说明、相关 Issue 引用
|
||||
|
||||
### Requirement: 包含行为准则文件
|
||||
项目根目录 SHALL 包含 `CODE_OF_CONDUCT.md`,采用 Contributor Covenant v2.1 中文版,明确社区行为规范和违规处理方式。
|
||||
|
||||
#### Scenario: 行为准则文件可访问
|
||||
- **WHEN** 贡献者查看项目根目录
|
||||
- **THEN** `CODE_OF_CONDUCT.md` SHALL 存在,包含社区行为规范、适用范围、执行说明、联系方式
|
||||
|
||||
### Requirement: 包含安全漏洞报告政策
|
||||
项目根目录 SHALL 包含 `SECURITY.md`,说明如何负责任地披露安全漏洞、支持的版本范围和响应时间承诺。
|
||||
|
||||
#### Scenario: 安全政策文件包含报告方式
|
||||
- **WHEN** 安全研究者发现漏洞
|
||||
- **THEN** `SECURITY.md` SHALL 提供私下联系方式(邮件或 GitHub Security Advisory),不要求通过公开 Issue 上报
|
||||
29
openspec/specs/oss-readme-polish/spec.md
Normal file
29
openspec/specs/oss-readme-polish/spec.md
Normal file
@ -0,0 +1,29 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: README 顶部展示状态徽章
|
||||
`README.md` 顶部(标题下方)SHALL 包含至少三枚徽章:Python 版本要求、License 类型、CI 状态,采用 shields.io 或 GitHub Actions 徽章格式。
|
||||
|
||||
#### Scenario: 徽章在 GitHub 页面正常渲染
|
||||
- **WHEN** 访问项目 GitHub 主页
|
||||
- **THEN** README 顶部 SHALL 显示可点击的 Python、MIT License、CI 状态徽章,链接指向对应资源
|
||||
|
||||
### Requirement: README 项目结构图反映实际代码
|
||||
`README.md` 中的「项目结构」章节 SHALL 反映迁移后的实际目录结构,包含 `services/`(含所有迁移后文件)和 `ui/`(含 `app.py`、`tab_create.py`)的正确层级。
|
||||
|
||||
#### Scenario: 项目结构与 ls 输出一致
|
||||
- **WHEN** 开发者对照 README 查看实际文件目录
|
||||
- **THEN** README 的结构图 SHALL 与实际 `Get-ChildItem` / `ls` 输出一致,无过时文件或缺失目录
|
||||
|
||||
### Requirement: README 不包含 your-username 占位符
|
||||
`README.md` 中所有 `your-username` 占位符 SHALL 替换为实际仓库路径说明或格式示例,使克隆/安装命令可直接复制使用。
|
||||
|
||||
#### Scenario: 安装命令无需手动替换占位符
|
||||
- **WHEN** 用户复制 README 中的 `git clone` 命令
|
||||
- **THEN** 命令 SHALL 包含实际仓库 URL 或明确的 `<your-github-username>` 格式提示,不出现 `your-username` 字符串
|
||||
|
||||
### Requirement: README 使用指南与当前 UI 结构匹配
|
||||
`README.md` 中的「使用指南」和「首次使用流程」章节 SHALL 引用当前正确的 Tab 名称和操作路径,与 `ui/app.py` 实际 Tab 顺序保持一致(⚙️ 配置 Tab 已迁移,不再是「展开全局设置折叠块」)。
|
||||
|
||||
#### Scenario: 首次使用步骤描述与 UI 一致
|
||||
- **WHEN** 新用户按照 README「首次使用流程」操作
|
||||
- **THEN** README 中描述的 Tab 名称和操作入口 SHALL 与实际 Gradio UI 一致,用户无需猜测
|
||||
49
openspec/specs/project-structure/spec.md
Normal file
49
openspec/specs/project-structure/spec.md
Normal file
@ -0,0 +1,49 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 根目录只包含正式项目文件
|
||||
项目根目录 SHALL 不包含备份文件、一次性测试脚本、个人媒体资源或临时记录文件。具体地:
|
||||
|
||||
- `*_backup.py` 命名的文件 SHALL 不存在于根目录
|
||||
- `config copy.json`(或同类配置副本)SHALL 不存在
|
||||
- `_test_*.py` 命名的一次性测试脚本 SHALL 不存在于根目录
|
||||
- `*.log` 运行日志文件 SHALL 不进入版本控制(由 `.gitignore` 保证)
|
||||
- 个人图片(`.png`、`.jpg` 等媒体文件)SHALL 不散落在根目录,统一归入 `assets/` 下对应子目录
|
||||
|
||||
#### Scenario: 克隆仓库后根目录无冗余文件
|
||||
- **WHEN** 开发者执行 `git clone` 并查看根目录
|
||||
- **THEN** 根目录 SHALL 仅包含:`main.py`、服务模块文件(`*_service.py`、`*_client.py`、`*_manager.py`、`*_queue.py`)、标准配置(`config.example.json`、`requirements.txt`、`Dockerfile`、`docker-compose.yml`、`.gitignore`、`.dockerignore`)、自动启动脚本(`_autostart.*`)、文档(`README.md`、`CHANGELOG.md`、`CONTRIBUTING.md`、`mcp.md`)
|
||||
|
||||
#### Scenario: 备份文件不出现在版本控制
|
||||
- **WHEN** 开发者执行 `git status` 或 `git ls-files`
|
||||
- **THEN** 输出中 SHALL 不包含 `*_backup.py`、`config copy.json` 等备份/副本文件
|
||||
|
||||
### Requirement: 媒体资源归入 assets/ 目录
|
||||
项目所需的图片等媒体资源 SHALL 存放于 `assets/` 目录下的对应子目录,根据用途分类:
|
||||
|
||||
- 换脸/头像相关图片 SHALL 放入 `assets/faces/`
|
||||
- `assets/faces/` SHALL 被 `.gitignore` 覆盖(不进入版本控制,属隐私数据)
|
||||
|
||||
#### Scenario: 换脸图片存放路径符合规范
|
||||
- **WHEN** 用户配置换脸头像功能
|
||||
- **THEN** 头像文件 SHALL 存放于 `assets/faces/` 目录,而非项目根目录
|
||||
|
||||
#### Scenario: assets/faces/ 不进入版本控制
|
||||
- **WHEN** 开发者执行 `git status`
|
||||
- **THEN** `assets/faces/` 目录下的文件 SHALL 显示为已忽略(不出现在 staged 或 unstaged 区域)
|
||||
|
||||
### Requirement: .gitignore 覆盖所有非版本控制内容
|
||||
项目 `.gitignore` SHALL 包含以下规则类别,且每类规则 SHALL 有注释说明用途:
|
||||
|
||||
- Python 编译产物(`__pycache__/`、`*.py[cod]`)
|
||||
- 虚拟环境目录(`.venv/`、`venv/`)
|
||||
- 敏感配置(`config.json`、`cookies.json`、`*.cookie`)
|
||||
- 运行日志(`*.log`、`logs/`)
|
||||
- 备份与副本文件(`*_backup.py`、`config copy.json`)
|
||||
- 个人媒体资产(`assets/faces/`)
|
||||
- IDE 配置(`.vscode/`、`.idea/`)
|
||||
- 系统文件(`.DS_Store`、`Thumbs.db`)
|
||||
- 工作空间输出目录(`xhs_workspace/`)
|
||||
|
||||
#### Scenario: 新增备份文件不被 git 追踪
|
||||
- **WHEN** 开发者在根目录创建 `main_v2_backup.py`
|
||||
- **THEN** `git status` SHALL 将其显示为已忽略文件
|
||||
3
requirements-dev.txt
Normal file
3
requirements-dev.txt
Normal file
@ -0,0 +1,3 @@
|
||||
# 开发与 CI 依赖(不包含生产运行时依赖)
|
||||
# 安装命令:pip install -r requirements-dev.txt
|
||||
ruff>=0.4.0
|
||||
@ -14,12 +14,12 @@ _STARTUP_REG_KEY = r"Software\Microsoft\Windows\CurrentVersion\Run"
|
||||
|
||||
def _get_startup_script_path() -> str:
|
||||
"""获取启动脚本路径(.vbs 静默启动,不弹黑窗)"""
|
||||
return os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "_autostart.vbs")
|
||||
return os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "scripts", "_autostart.vbs")
|
||||
|
||||
|
||||
def _get_startup_bat_path() -> str:
|
||||
"""获取启动 bat 路径"""
|
||||
return os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "_autostart.bat")
|
||||
return os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "scripts", "_autostart.bat")
|
||||
|
||||
|
||||
def _create_startup_scripts():
|
||||
@ -35,6 +35,7 @@ def _create_startup_scripts():
|
||||
|
||||
# 创建 bat
|
||||
bat_path = _get_startup_bat_path()
|
||||
os.makedirs(os.path.dirname(bat_path), exist_ok=True)
|
||||
bat_content = f"""@echo off
|
||||
cd /d "{app_dir}"
|
||||
"{venv_python}" "{main_script}"
|
||||
|
||||
@ -13,7 +13,8 @@ logger = logging.getLogger(__name__)
|
||||
_KEYRING_PLACEHOLDER = "[keyring]"
|
||||
_KEYRING_SERVICE = "autobot_xhs"
|
||||
|
||||
CONFIG_FILE = "config.json"
|
||||
_PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
CONFIG_FILE = os.path.join(_PROJECT_ROOT, "config", "config.json")
|
||||
OUTPUT_DIR = "xhs_workspace"
|
||||
|
||||
DEFAULT_CONFIG = {
|
||||
@ -8,10 +8,10 @@ import logging
|
||||
|
||||
import gradio as gr
|
||||
|
||||
from config_manager import ConfigManager
|
||||
from llm_service import LLMService
|
||||
from sd_service import SDService, get_model_profile_info
|
||||
from mcp_client import get_mcp_client
|
||||
from .config_manager import ConfigManager
|
||||
from .llm_service import LLMService
|
||||
from .sd_service import SDService, get_model_profile_info
|
||||
from .mcp_client import get_mcp_client
|
||||
|
||||
logger = logging.getLogger("autobot")
|
||||
cfg = ConfigManager()
|
||||
|
||||
@ -11,12 +11,12 @@ import logging
|
||||
|
||||
from PIL import Image
|
||||
|
||||
from config_manager import ConfigManager, OUTPUT_DIR
|
||||
from llm_service import LLMService
|
||||
from sd_service import SDService, get_sd_preset
|
||||
from mcp_client import get_mcp_client
|
||||
from services.connection import _get_llm_config
|
||||
from services.persona import _resolve_persona
|
||||
from .config_manager import ConfigManager, OUTPUT_DIR
|
||||
from .llm_service import LLMService
|
||||
from .sd_service import SDService, get_sd_preset
|
||||
from .mcp_client import get_mcp_client
|
||||
from .connection import _get_llm_config
|
||||
from .persona import _resolve_persona
|
||||
|
||||
logger = logging.getLogger("autobot")
|
||||
cfg = ConfigManager()
|
||||
|
||||
@ -6,10 +6,10 @@ import logging
|
||||
|
||||
import gradio as gr
|
||||
|
||||
from mcp_client import get_mcp_client
|
||||
from llm_service import LLMService
|
||||
from services.connection import _get_llm_config
|
||||
from services.hotspot import _pick_from_cache, _set_cache, _get_cache
|
||||
from .mcp_client import get_mcp_client
|
||||
from .llm_service import LLMService
|
||||
from .connection import _get_llm_config
|
||||
from .hotspot import _pick_from_cache, _set_cache, _get_cache
|
||||
|
||||
logger = logging.getLogger("autobot")
|
||||
|
||||
|
||||
@ -7,10 +7,10 @@ import logging
|
||||
|
||||
import gradio as gr
|
||||
|
||||
from llm_service import LLMService
|
||||
from mcp_client import get_mcp_client
|
||||
from services.connection import _get_llm_config
|
||||
from services.persona import _resolve_persona
|
||||
from .llm_service import LLMService
|
||||
from .mcp_client import get_mcp_client
|
||||
from .connection import _get_llm_config
|
||||
from .persona import _resolve_persona
|
||||
|
||||
logger = logging.getLogger("autobot")
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@ services/persona.py
|
||||
"""
|
||||
import random
|
||||
import logging
|
||||
from config_manager import ConfigManager
|
||||
from .config_manager import ConfigManager
|
||||
|
||||
logger = logging.getLogger("autobot")
|
||||
cfg = ConfigManager()
|
||||
|
||||
@ -9,7 +9,7 @@ import logging
|
||||
import matplotlib
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from mcp_client import get_mcp_client
|
||||
from .mcp_client import get_mcp_client
|
||||
|
||||
_font_candidates = ["Microsoft YaHei", "SimHei", "PingFang SC", "WenQuanYi Micro Hei"]
|
||||
for _fn in _font_candidates:
|
||||
|
||||
@ -6,17 +6,17 @@ import os
|
||||
import time
|
||||
import logging
|
||||
|
||||
from config_manager import ConfigManager, OUTPUT_DIR
|
||||
from publish_queue import (
|
||||
from .config_manager import ConfigManager, OUTPUT_DIR
|
||||
from .publish_queue import (
|
||||
PublishQueue, QueuePublisher,
|
||||
STATUS_DRAFT, STATUS_APPROVED, STATUS_SCHEDULED, STATUS_PUBLISHING,
|
||||
STATUS_PUBLISHED, STATUS_FAILED, STATUS_REJECTED, STATUS_LABELS,
|
||||
)
|
||||
from mcp_client import get_mcp_client
|
||||
from services.connection import _get_llm_config
|
||||
from services.persona import DEFAULT_TOPICS, DEFAULT_STYLES, _resolve_persona
|
||||
from services.content import generate_copy, generate_images
|
||||
from services.rate_limiter import _increment_stat, _clear_error_streak
|
||||
from .mcp_client import get_mcp_client
|
||||
from .connection import _get_llm_config
|
||||
from .persona import DEFAULT_TOPICS, DEFAULT_STYLES, _resolve_persona
|
||||
from .content import generate_copy, generate_images
|
||||
from .rate_limiter import _increment_stat, _clear_error_streak
|
||||
|
||||
cfg = ConfigManager()
|
||||
logger = logging.getLogger("autobot")
|
||||
|
||||
@ -12,23 +12,23 @@ from datetime import datetime
|
||||
|
||||
from PIL import Image
|
||||
|
||||
from config_manager import ConfigManager, OUTPUT_DIR
|
||||
from llm_service import LLMService
|
||||
from sd_service import SDService
|
||||
from mcp_client import get_mcp_client
|
||||
from services.rate_limiter import (
|
||||
from .config_manager import ConfigManager, OUTPUT_DIR
|
||||
from .llm_service import LLMService
|
||||
from .sd_service import SDService
|
||||
from .mcp_client import get_mcp_client
|
||||
from .rate_limiter import (
|
||||
_op_history, _daily_stats, DAILY_LIMITS,
|
||||
_reset_daily_stats_if_needed,
|
||||
_check_daily_limit, _increment_stat, _record_error,
|
||||
_clear_error_streak, _is_in_cooldown, _is_in_operating_hours,
|
||||
_get_stats_summary,
|
||||
)
|
||||
from services.persona import (
|
||||
from .persona import (
|
||||
DEFAULT_TOPICS, DEFAULT_STYLES, DEFAULT_COMMENT_KEYWORDS,
|
||||
get_persona_keywords, _resolve_persona,
|
||||
)
|
||||
from services.connection import _get_llm_config
|
||||
from analytics_service import AnalyticsService
|
||||
from .connection import _get_llm_config
|
||||
from .analytics_service import AnalyticsService
|
||||
|
||||
cfg = ConfigManager()
|
||||
logger = logging.getLogger("autobot")
|
||||
@ -51,7 +51,7 @@ _auto_running = threading.Event()
|
||||
_auto_thread: threading.Thread | None = None
|
||||
_auto_log: list[str] = []
|
||||
|
||||
from services.rate_limiter import (
|
||||
from .rate_limiter import (
|
||||
_op_history, _daily_stats, DAILY_LIMITS,
|
||||
_reset_daily_stats_if_needed,
|
||||
_check_daily_limit, _increment_stat, _record_error,
|
||||
@ -59,7 +59,7 @@ from services.rate_limiter import (
|
||||
_get_stats_summary,
|
||||
)
|
||||
|
||||
from services.persona import (
|
||||
from .persona import (
|
||||
DEFAULT_PERSONAS, RANDOM_PERSONA_LABEL, PERSONA_POOL_MAP,
|
||||
DEFAULT_TOPICS, DEFAULT_STYLES, DEFAULT_COMMENT_KEYWORDS,
|
||||
_match_persona_pools, get_persona_topics, get_persona_keywords,
|
||||
@ -1068,7 +1068,7 @@ def stop_learn_scheduler():
|
||||
# ==================================================
|
||||
# Windows 开机自启管理
|
||||
# ==================================================
|
||||
from services.autostart import (
|
||||
from .autostart import (
|
||||
is_autostart_enabled, enable_autostart, disable_autostart, toggle_autostart,
|
||||
)
|
||||
|
||||
|
||||
@ -19,7 +19,7 @@ logger = logging.getLogger(__name__)
|
||||
SD_TIMEOUT = 1800 # 图片生成可能需要较长时间
|
||||
|
||||
# 头像文件默认保存路径
|
||||
FACE_IMAGE_PATH = os.path.join(os.path.dirname(__file__), "my_face.png")
|
||||
FACE_IMAGE_PATH = os.path.join(os.path.dirname(__file__), "assets", "faces", "my_face.png")
|
||||
|
||||
# ==================== 多模型配置系统 ====================
|
||||
# 每个模型的最优参数、prompt 增强词、负面提示词、三档预设
|
||||
@ -8,9 +8,9 @@ import logging
|
||||
|
||||
import gradio as gr
|
||||
|
||||
from config_manager import ConfigManager
|
||||
from sd_service import SDService, DEFAULT_NEGATIVE, FACE_IMAGE_PATH, SD_PRESET_NAMES, get_sd_preset, SD_MODEL_PROFILES
|
||||
from analytics_service import AnalyticsService
|
||||
from services.config_manager import ConfigManager
|
||||
from services.sd_service import SDService, DEFAULT_NEGATIVE, FACE_IMAGE_PATH, SD_PRESET_NAMES, get_sd_preset, SD_MODEL_PROFILES
|
||||
from services.analytics_service import AnalyticsService
|
||||
from ui.tab_create import build_tab
|
||||
|
||||
from services.connection import (
|
||||
@ -53,7 +53,7 @@ from services.queue_ops import (
|
||||
)
|
||||
from services.autostart import is_autostart_enabled, toggle_autostart
|
||||
from services.content import generate_copy, generate_images, one_click_export, publish_to_xhs
|
||||
from publish_queue import STATUS_LABELS
|
||||
from services.publish_queue import STATUS_LABELS
|
||||
|
||||
logger = logging.getLogger("autobot")
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user