项目初始化:小说创作工具集
- 创建飞书同步工具 (Python版) - 创建字数统计工具 - 创建章节生成器 - 创建番茄黄金三章模板 - 完善项目文档和结构 - 配置完整的工具链
This commit is contained in:
commit
5ae289decb
96
PROJECT.md
Normal file
96
PROJECT.md
Normal file
@ -0,0 +1,96 @@
|
||||
# 小说创作工具项目
|
||||
|
||||
## 项目信息
|
||||
- **名称**: 小说创作工具集
|
||||
- **类型**: 工具、脚本、自动化
|
||||
- **状态**: 持续开发中
|
||||
- **目标**: 提供高效的小说创作工具链
|
||||
- **作者**: 唐天洛
|
||||
- **创建时间**: 2026年3月30日
|
||||
|
||||
## 项目目标
|
||||
1. **提升效率**: 自动化重复性工作
|
||||
2. **质量控制**: 辅助质量检查和优化
|
||||
3. **进度管理**: 跟踪创作进度和数据
|
||||
4. **同步备份**: 管理多平台同步和备份
|
||||
|
||||
## 核心工具
|
||||
|
||||
### 1. 创作管理工具
|
||||
- **章节生成器**: 结构化章节生成
|
||||
- **大纲助手**: 大纲规划和梳理
|
||||
- **人物管理**: 人物设定和关系管理
|
||||
- **情节设计**: 爽点设计和情节编排
|
||||
|
||||
### 2. 质量检查工具
|
||||
- **字数统计**: 章节字数、总字数统计
|
||||
- **节奏分析**: 节奏快慢和爽点密度分析
|
||||
- **对话检查**: 对话占比和口语化检查
|
||||
- **逻辑校验**: 时间线、空间位置逻辑检查
|
||||
|
||||
### 3. 同步工具
|
||||
- **飞书同步**: 自动同步到飞书文档
|
||||
- **Git同步**: 版本控制和云端备份
|
||||
- **多平台适配**: 适配番茄、起点等平台格式
|
||||
- **数据同步**: 进度数据自动同步
|
||||
|
||||
### 4. 自动化工具
|
||||
- **定时检查**: 自动检查创作进度
|
||||
- **模板应用**: 快速应用创作模板
|
||||
- **文件整理**: 自动整理和归档文件
|
||||
- **数据分析**: 创作数据分析和报告
|
||||
|
||||
## 项目结构
|
||||
```
|
||||
小说工具/
|
||||
├── PROJECT.md # 项目说明(本文件)
|
||||
├── README.md # 详细使用说明
|
||||
├── generators/ # 生成器工具
|
||||
│ ├── chapter_generator.py
|
||||
│ ├── outline_generator.py
|
||||
│ └── character_generator.py
|
||||
├── analyzers/ # 分析工具
|
||||
│ ├── word_count.py
|
||||
│ ├── rhythm_analyzer.py
|
||||
│ └── dialogue_checker.py
|
||||
├── sync_tools/ # 同步工具
|
||||
│ ├── feishu_sync.py
|
||||
│ ├── git_sync.py
|
||||
│ └── platform_adapter.py
|
||||
├── automation/ # 自动化脚本
|
||||
│ ├── daily_check.sh
|
||||
│ ├── file_organizer.py
|
||||
│ └── data_analyzer.py
|
||||
├── templates/ # 模板文件
|
||||
│ ├── 番茄黄金三章.md
|
||||
│ ├── 玄幻大纲模板.md
|
||||
│ └── 悬疑情节模板.md
|
||||
└── configs/ # 配置文件
|
||||
├── feishu_config.json
|
||||
├── git_config.json
|
||||
└── platform_config.json
|
||||
```
|
||||
|
||||
## 当前已有工具
|
||||
1. **feishu_novel_sync.sh**: 飞书小说同步脚本
|
||||
2. **sync_novel_to_feishu.py**: Python版同步脚本
|
||||
3. **inkos_monitor.sh**: inkos创作监控脚本
|
||||
4. **番茄创作工作流.md**: 番茄平台创作工作流文档
|
||||
|
||||
## 开发计划
|
||||
1. **第一阶段**: 完善已有工具,统一接口
|
||||
2. **第二阶段**: 开发Web界面,方便使用
|
||||
3. **第三阶段**: 集成AI辅助创作功能
|
||||
4. **第四阶段**: 开发团队协作功能
|
||||
|
||||
## 使用说明
|
||||
1. **安装依赖**: 根据工具要求安装Python/Shell依赖
|
||||
2. **配置环境**: 配置飞书、Git等平台API
|
||||
3. **定时运行**: 设置定时任务自动运行
|
||||
4. **监控反馈**: 监控运行结果,调整配置
|
||||
|
||||
## 项目价值
|
||||
1. **个人效率**: 大幅提升个人创作效率
|
||||
2. **质量保障**: 系统化保障作品质量
|
||||
3. **数据驱动**: 基于数据的创作决策
|
||||
4. **可扩展性**: 可扩展到团队协作和多平台
|
||||
95
README.md
Normal file
95
README.md
Normal file
@ -0,0 +1,95 @@
|
||||
# 小说创作工具集
|
||||
|
||||
## 项目简介
|
||||
为番茄小说创作者提供完整的工具链,涵盖创作、管理、同步、分析全流程。
|
||||
|
||||
## 核心功能
|
||||
### 1. 创作管理
|
||||
- 章节生成器
|
||||
- 大纲助手
|
||||
- 人物管理
|
||||
- 情节设计
|
||||
|
||||
### 2. 质量检查
|
||||
- 字数统计
|
||||
- 节奏分析
|
||||
- 对话检查
|
||||
- 逻辑校验
|
||||
|
||||
### 3. 同步工具
|
||||
- 飞书同步
|
||||
- Git同步
|
||||
- 多平台适配
|
||||
- 数据同步
|
||||
|
||||
### 4. 自动化
|
||||
- 定时检查
|
||||
- 模板应用
|
||||
- 文件整理
|
||||
- 数据分析
|
||||
|
||||
## 快速开始
|
||||
### 安装
|
||||
```bash
|
||||
# 克隆项目
|
||||
git clone https://gitea.nevadalice.top:226/liyuchen/novel-tools.git
|
||||
|
||||
# 安装依赖
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
### 使用
|
||||
```bash
|
||||
# 创建新小说项目
|
||||
./create_novel_project.sh 末日重生 "末日重生-开局囤货十亿物资" 末世重生
|
||||
|
||||
# 统计章节
|
||||
cd 末日重生 && ./tools/章节统计.sh
|
||||
|
||||
# 同步到飞书
|
||||
python sync_tools/feishu_sync.py
|
||||
```
|
||||
|
||||
## 项目结构
|
||||
```
|
||||
小说工具/
|
||||
├── README.md # 项目说明
|
||||
├── create_novel_project.sh # 项目创建工具
|
||||
├── generators/ # 生成器
|
||||
├── analyzers/ # 分析工具
|
||||
├── sync_tools/ # 同步工具
|
||||
├── automation/ # 自动化脚本
|
||||
├── templates/ # 模板文件
|
||||
└── configs/ # 配置文件
|
||||
```
|
||||
|
||||
## 已有工具
|
||||
1. `create_novel_project.sh` - 快速创建小说项目
|
||||
2. `feishu_novel_sync.sh` - 飞书小说同步(原版)
|
||||
3. `sync_novel_to_feishu.py` - Python版同步
|
||||
4. `inkos_monitor.sh` - inkos创作监控
|
||||
|
||||
## 开发计划
|
||||
### 第一阶段(已完成)
|
||||
- [x] 基础工具框架
|
||||
- [x] 项目创建工具
|
||||
- [x] 基础同步工具
|
||||
|
||||
### 第二阶段(进行中)
|
||||
- [ ] 章节生成器
|
||||
- [ ] 质量分析工具
|
||||
- [ ] Web管理界面
|
||||
|
||||
### 第三阶段(规划中)
|
||||
- [ ] AI辅助创作
|
||||
- [ ] 数据分析平台
|
||||
- [ ] 团队协作功能
|
||||
|
||||
## 贡献指南
|
||||
1. Fork本项目
|
||||
2. 创建功能分支
|
||||
3. 提交Pull Request
|
||||
4. 通过代码审查
|
||||
|
||||
## 许可证
|
||||
MIT License
|
||||
199
analyzers/word_count.py
Executable file
199
analyzers/word_count.py
Executable file
@ -0,0 +1,199 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
小说字数统计工具
|
||||
统计章节字数、总字数、进度等
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
from datetime import datetime
|
||||
import json
|
||||
|
||||
class WordCounter:
|
||||
def __init__(self):
|
||||
self.total_words = 0
|
||||
self.total_chars = 0
|
||||
self.chapter_stats = []
|
||||
|
||||
def count_file(self, file_path):
|
||||
"""统计单个文件"""
|
||||
try:
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
|
||||
# 计算字数(中文字符+英文单词)
|
||||
chinese_chars = len(re.findall(r'[\u4e00-\u9fff]', content))
|
||||
english_words = len(re.findall(r'\b[a-zA-Z]+\b', content))
|
||||
total_words = chinese_chars + english_words
|
||||
|
||||
# 计算总字符数
|
||||
total_chars = len(content)
|
||||
|
||||
# 计算段落数
|
||||
paragraphs = len([p for p in content.split('\n\n') if p.strip()])
|
||||
|
||||
# 计算对话行数
|
||||
dialogue_lines = len(re.findall(r'["「][^"」]+["」]', content))
|
||||
|
||||
return {
|
||||
"file": file_path,
|
||||
"words": total_words,
|
||||
"chars": total_chars,
|
||||
"paragraphs": paragraphs,
|
||||
"dialogue_lines": dialogue_lines,
|
||||
"dialogue_ratio": round(dialogue_lines / max(paragraphs, 1), 2)
|
||||
}
|
||||
except Exception as e:
|
||||
print(f"统计文件失败 {file_path}: {e}")
|
||||
return None
|
||||
|
||||
def count_project(self, project_path):
|
||||
"""统计整个项目"""
|
||||
chapters_dir = os.path.join(project_path, "chapters")
|
||||
|
||||
if not os.path.exists(chapters_dir):
|
||||
print(f"章节目录不存在: {chapters_dir}")
|
||||
return None
|
||||
|
||||
# 获取所有章节文件
|
||||
chapter_files = []
|
||||
for root, dirs, files in os.walk(chapters_dir):
|
||||
for file in files:
|
||||
if file.endswith('.md'):
|
||||
chapter_files.append(os.path.join(root, file))
|
||||
|
||||
if not chapter_files:
|
||||
print("没有找到章节文件")
|
||||
return None
|
||||
|
||||
# 按文件名排序
|
||||
chapter_files.sort()
|
||||
|
||||
# 统计每个章节
|
||||
self.chapter_stats = []
|
||||
self.total_words = 0
|
||||
self.total_chars = 0
|
||||
|
||||
for chapter_file in chapter_files:
|
||||
stats = self.count_file(chapter_file)
|
||||
if stats:
|
||||
self.chapter_stats.append(stats)
|
||||
self.total_words += stats['words']
|
||||
self.total_chars += stats['chars']
|
||||
|
||||
return {
|
||||
"project": project_path,
|
||||
"total_chapters": len(self.chapter_stats),
|
||||
"total_words": self.total_words,
|
||||
"total_chars": self.total_chars,
|
||||
"avg_words_per_chapter": round(self.total_words / max(len(self.chapter_stats), 1)),
|
||||
"chapters": self.chapter_stats
|
||||
}
|
||||
|
||||
def generate_report(self, project_path, output_path=None):
|
||||
"""生成统计报告"""
|
||||
stats = self.count_project(project_path)
|
||||
if not stats:
|
||||
return None
|
||||
|
||||
# 番茄平台标准
|
||||
tomato_standard = 2500 # 每章标准字数
|
||||
daily_target = 4000 # 日更目标
|
||||
|
||||
# 计算进度
|
||||
completed_chapters = stats['total_chapters']
|
||||
total_words = stats['total_words']
|
||||
avg_words = stats['avg_words_per_chapter']
|
||||
|
||||
# 评估
|
||||
if avg_words < 2000:
|
||||
word_rating = "⚠️ 字数偏少"
|
||||
elif avg_words < 2500:
|
||||
word_rating = "✅ 符合标准"
|
||||
elif avg_words < 3500:
|
||||
word_rating = "✅ 字数充足"
|
||||
else:
|
||||
word_rating = "⚠️ 字数偏多"
|
||||
|
||||
# 对话占比评估
|
||||
avg_dialogue_ratio = sum(s['dialogue_ratio'] for s in self.chapter_stats) / len(self.chapter_stats)
|
||||
if avg_dialogue_ratio < 0.3:
|
||||
dialogue_rating = "⚠️ 对话偏少(影响听书分成)"
|
||||
elif avg_dialogue_ratio < 0.5:
|
||||
dialogue_rating = "✅ 对话适中"
|
||||
else:
|
||||
dialogue_rating = "✅ 对话丰富(适合听书)"
|
||||
|
||||
# 生成报告
|
||||
report = f"""
|
||||
# 小说字数统计报告
|
||||
|
||||
## 项目信息
|
||||
- 项目路径: {project_path}
|
||||
- 统计时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
|
||||
- 章节数量: {completed_chapters} 章
|
||||
- 总字数: {total_words} 字
|
||||
- 总字符数: {stats['total_chars']} 字符
|
||||
|
||||
## 章节统计
|
||||
- 平均每章字数: {avg_words} 字
|
||||
- 字数评估: {word_rating}
|
||||
- 平均对话占比: {avg_dialogue_ratio:.1%}
|
||||
- 对话评估: {dialogue_rating}
|
||||
|
||||
## 番茄平台适配
|
||||
### 字数要求
|
||||
- 标准章节字数: 2500-3500字
|
||||
- 当前平均: {avg_words} 字
|
||||
- 状态: {"符合" if 2000 <= avg_words <= 3500 else "需要调整"}
|
||||
|
||||
### 更新要求
|
||||
- 日更全勤要求: 4000字/天
|
||||
- 当前总字数: {total_words} 字
|
||||
- 相当于: {total_words // 4000} 天的全勤更新量
|
||||
|
||||
### 听书优化
|
||||
- 推荐对话占比: >30%
|
||||
- 当前对话占比: {avg_dialogue_ratio:.1%}
|
||||
- 状态: {"适合听书" if avg_dialogue_ratio >= 0.3 else "需要增加对话"}
|
||||
|
||||
## 详细章节数据
|
||||
"""
|
||||
# 添加每个章节的详细数据
|
||||
for i, chapter in enumerate(self.chapter_stats, 1):
|
||||
chapter_name = os.path.basename(chapter['file']).replace('.md', '')
|
||||
report += f"\n### 第{i}章: {chapter_name}\n"
|
||||
report += f"- 字数: {chapter['words']} 字\n"
|
||||
report += f"- 字符: {chapter['chars']} 字符\n"
|
||||
report += f"- 段落: {chapter['paragraphs']} 段\n"
|
||||
report += f"- 对话行: {chapter['dialogue_lines']} 行\n"
|
||||
report += f"- 对话占比: {chapter['dialogue_ratio']:.1%}\n"
|
||||
|
||||
# 保存报告
|
||||
if output_path:
|
||||
os.makedirs(os.path.dirname(output_path), exist_ok=True)
|
||||
with open(output_path, 'w', encoding='utf-8') as f:
|
||||
f.write(report)
|
||||
|
||||
print(f"报告已保存: {output_path}")
|
||||
|
||||
return report
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(description="小说字数统计工具")
|
||||
parser.add_argument("--project", help="项目路径", default=".")
|
||||
parser.add_argument("--output", help="输出报告路径")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
counter = WordCounter()
|
||||
report = counter.generate_report(args.project, args.output)
|
||||
|
||||
if report:
|
||||
print(report)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
205
create_novel_project.sh
Executable file
205
create_novel_project.sh
Executable file
@ -0,0 +1,205 @@
|
||||
#!/bin/bash
|
||||
# 小说项目创建工具
|
||||
# 用法: ./create_novel_project.sh 项目名称 "小说名称" 类型
|
||||
|
||||
if [ $# -lt 3 ]; then
|
||||
echo "用法: $0 项目目录名 小说名称 类型"
|
||||
echo "示例: $0 末日重生_囤货 \"末日重生-开局囤货十亿物资\" 末世重生"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PROJECT_DIR="$1"
|
||||
NOVEL_NAME="$2"
|
||||
NOVEL_TYPE="$3"
|
||||
PROJECTS_ROOT="/root/.openclaw/workspace/projects"
|
||||
TARGET_DIR="$PROJECTS_ROOT/$PROJECT_DIR"
|
||||
|
||||
# 检查是否已存在
|
||||
if [ -d "$TARGET_DIR" ]; then
|
||||
echo "错误: 项目目录 '$PROJECT_DIR' 已存在"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 创建项目目录
|
||||
echo "创建项目: $NOVEL_NAME"
|
||||
mkdir -p "$TARGET_DIR"
|
||||
cd "$TARGET_DIR"
|
||||
|
||||
# 初始化Git仓库
|
||||
git init
|
||||
git config user.name "唐天洛"
|
||||
git config user.email "563415956@qq.com"
|
||||
|
||||
# 创建基础目录结构
|
||||
mkdir -p chapters outlines assets/characters assets/settings assets/plots tools sync progress
|
||||
|
||||
# 创建PROJECT.md
|
||||
cat > PROJECT.md << EOF
|
||||
# 《$NOVEL_NAME》项目
|
||||
|
||||
## 项目信息
|
||||
- **书名**: $NOVEL_NAME
|
||||
- **类型**: $NOVEL_TYPE
|
||||
- **状态**: 新项目
|
||||
- **目标平台**: 番茄小说
|
||||
- **作者**: 唐天洛
|
||||
- **创建时间**: $(date '+%Y年%m月%d日')
|
||||
|
||||
## 项目结构
|
||||
\`\`\`
|
||||
$PROJECT_DIR/
|
||||
├── PROJECT.md # 项目说明(本文件)
|
||||
├── README.md # 项目详细介绍
|
||||
├── chapters/ # 章节文件
|
||||
├── outlines/ # 大纲和规划
|
||||
├── assets/ # 资源文件
|
||||
│ ├── characters/ # 人物资料
|
||||
│ ├── settings/ # 世界观设定
|
||||
│ └── plots/ # 情节设计
|
||||
├── tools/ # 项目工具
|
||||
├── sync/ # 同步配置
|
||||
└── progress/ # 进度跟踪
|
||||
\`\`\`
|
||||
|
||||
## 当前进度
|
||||
- **章节**: 0章
|
||||
- **字数**: 0字
|
||||
- **状态**: 规划阶段
|
||||
|
||||
## 平台要求
|
||||
- **番茄黄金三章**: 待完成
|
||||
- **章节结构**: 承接→推进→高潮→钩子
|
||||
- **字数要求**: 每章2500-3500字
|
||||
- **更新要求**: 日更4000字(全勤奖最低要求)
|
||||
|
||||
## 创作计划
|
||||
1. **第一阶段**: 人物设定和世界观构建
|
||||
2. **第二阶段**: 大纲规划和爽点设计
|
||||
3. **第三阶段**: 黄金三章创作
|
||||
4. **第四阶段**: 持续更新
|
||||
EOF
|
||||
|
||||
# 创建README.md
|
||||
cat > README.md << EOF
|
||||
# 《$NOVEL_NAME》
|
||||
|
||||
## 项目简介
|
||||
$NOVEL_TYPE题材小说,$NOVEL_NAME
|
||||
|
||||
## 项目状态
|
||||
- 🟡 规划阶段
|
||||
- 📅 创建时间: $(date '+%Y-%m-%d')
|
||||
- ✍️ 作者: 唐天洛
|
||||
|
||||
## 快速开始
|
||||
1. 在 \`outlines/\` 目录下规划大纲
|
||||
2. 在 \`assets/\` 目录下创建人物和设定
|
||||
3. 在 \`chapters/\` 目录下创作章节
|
||||
4. 使用 \`tools/\` 中的工具进行管理
|
||||
|
||||
## 目录说明
|
||||
- \`chapters/\` - 章节文件 (.md格式)
|
||||
- \`outlines/\` - 大纲文件
|
||||
- \`assets/characters/\` - 人物设定
|
||||
- \`assets/settings/\` - 世界观设定
|
||||
- \`assets/plots/\` - 情节设计
|
||||
- \`tools/\` - 管理工具
|
||||
- \`sync/\` - 同步配置
|
||||
- \`progress/\` - 进度跟踪
|
||||
|
||||
## 更新日志
|
||||
- $(date '+%Y-%m-%d'): 项目创建
|
||||
EOF
|
||||
|
||||
# 创建.gitignore
|
||||
cat > .gitignore << EOF
|
||||
# 临时文件
|
||||
*.tmp
|
||||
*.log
|
||||
*.bak
|
||||
|
||||
# 系统文件
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# 编辑器文件
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
|
||||
# 配置文件(包含敏感信息)
|
||||
config.json
|
||||
*.key
|
||||
*.pem
|
||||
.env
|
||||
|
||||
# 大文件
|
||||
*.zip
|
||||
*.rar
|
||||
*.7z
|
||||
EOF
|
||||
|
||||
# 创建基础工具
|
||||
cat > tools/章节统计.sh << 'EOF'
|
||||
#!/bin/bash
|
||||
# 章节统计工具
|
||||
|
||||
echo "=== 章节统计 ==="
|
||||
echo "项目: 《'$NOVEL_NAME'》"
|
||||
echo "统计时间: $(date '+%Y-%m-%d %H:%M:%S')"
|
||||
echo ""
|
||||
|
||||
# 统计章节数量
|
||||
if [ -d "chapters" ]; then
|
||||
CHAPTER_COUNT=$(find chapters -name "*.md" | wc -l)
|
||||
echo "章节数量: $CHAPTER_COUNT"
|
||||
|
||||
# 统计总字数
|
||||
TOTAL_WORDS=0
|
||||
for file in chapters/*.md; do
|
||||
if [ -f "$file" ]; then
|
||||
WORDS=$(wc -w < "$file" 2>/dev/null || echo 0)
|
||||
TOTAL_WORDS=$((TOTAL_WORDS + WORDS))
|
||||
fi
|
||||
done
|
||||
echo "总字数: $TOTAL_WORDS"
|
||||
|
||||
# 显示最近章节
|
||||
echo "最近章节:"
|
||||
find chapters -name "*.md" -exec ls -lt {} + | head -5 | awk '{print $9}'
|
||||
else
|
||||
echo "chapters目录不存在"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=== 统计完成 ==="
|
||||
EOF
|
||||
|
||||
chmod +x tools/章节统计.sh
|
||||
|
||||
# 创建进度跟踪文件
|
||||
cat > progress/更新日志.md << EOF
|
||||
# 更新日志
|
||||
|
||||
## $(date '+%Y年%m月')
|
||||
### $(date '+%Y-%m-%d')
|
||||
- [x] 项目创建
|
||||
- [ ] 人物设定完成
|
||||
- [ ] 大纲规划完成
|
||||
- [ ] 第一章完成
|
||||
EOF
|
||||
|
||||
# 初始化提交
|
||||
git add .
|
||||
git commit -m "项目初始化: $NOVEL_NAME"
|
||||
|
||||
echo ""
|
||||
echo "✅ 项目创建完成: $TARGET_DIR"
|
||||
echo "📖 小说: $NOVEL_NAME"
|
||||
echo "📁 目录结构已创建"
|
||||
echo "🔧 Git仓库已初始化"
|
||||
echo ""
|
||||
echo "下一步:"
|
||||
echo "1. 在 outlines/ 目录下规划大纲"
|
||||
echo "2. 在 assets/ 目录下创建人物设定"
|
||||
echo "3. 开始创作第一章"
|
||||
204
generators/chapter_generator.py
Executable file
204
generators/chapter_generator.py
Executable file
@ -0,0 +1,204 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
章节生成器
|
||||
根据模板生成标准化的章节结构
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
from datetime import datetime
|
||||
|
||||
class ChapterGenerator:
|
||||
def __init__(self, template_path=None):
|
||||
"""初始化生成器"""
|
||||
self.template = self.load_template(template_path) or self.default_template()
|
||||
|
||||
def load_template(self, template_path):
|
||||
"""加载模板文件"""
|
||||
if template_path and os.path.exists(template_path):
|
||||
with open(template_path, 'r', encoding='utf-8') as f:
|
||||
return f.read()
|
||||
return None
|
||||
|
||||
def default_template(self):
|
||||
"""默认模板(番茄黄金三章模板)"""
|
||||
return """# 第{chapter_number}章:{chapter_title}
|
||||
|
||||
## 承接(100-300字)
|
||||
{setup}
|
||||
|
||||
## 推进(800-2000字)
|
||||
{development}
|
||||
|
||||
## 高潮(500-1000字)
|
||||
{climax}
|
||||
|
||||
## 钩子(100-300字)
|
||||
{hook}
|
||||
|
||||
---
|
||||
|
||||
**字数统计**: {word_count}字
|
||||
**更新时间**: {update_time}
|
||||
**作者备注**: {note}
|
||||
"""
|
||||
|
||||
def generate(self, chapter_number, chapter_title, setup, development, climax, hook, note=""):
|
||||
"""生成章节内容"""
|
||||
# 计算字数
|
||||
word_count = len(setup + development + climax + hook)
|
||||
|
||||
# 填充模板
|
||||
content = self.template.format(
|
||||
chapter_number=chapter_number,
|
||||
chapter_title=chapter_title,
|
||||
setup=setup,
|
||||
development=development,
|
||||
climax=climax,
|
||||
hook=hook,
|
||||
word_count=word_count,
|
||||
update_time=datetime.now().strftime('%Y-%m-%d %H:%M'),
|
||||
note=note or "正常更新"
|
||||
)
|
||||
|
||||
return content
|
||||
|
||||
def save_chapter(self, project_path, chapter_number, chapter_title, content, **kwargs):
|
||||
"""保存章节文件"""
|
||||
# 创建章节目录
|
||||
chapters_dir = os.path.join(project_path, "chapters")
|
||||
os.makedirs(chapters_dir, exist_ok=True)
|
||||
|
||||
# 生成文件名
|
||||
filename = f"ch{chapter_number:02d}-{chapter_title}.md"
|
||||
filepath = os.path.join(chapters_dir, filename)
|
||||
|
||||
# 保存文件
|
||||
with open(filepath, 'w', encoding='utf-8') as f:
|
||||
f.write(content)
|
||||
|
||||
print(f"章节已保存: {filepath}")
|
||||
return filepath
|
||||
|
||||
def generate_from_outline(self, project_path, outline_file):
|
||||
"""根据大纲文件生成章节"""
|
||||
if not os.path.exists(outline_file):
|
||||
print(f"大纲文件不存在: {outline_file}")
|
||||
return []
|
||||
|
||||
# 读取大纲
|
||||
with open(outline_file, 'r', encoding='utf-8') as f:
|
||||
outline_content = f.read()
|
||||
|
||||
# 解析大纲中的章节信息
|
||||
# 这里假设大纲有特定的格式
|
||||
chapters = []
|
||||
|
||||
# 简单示例:解析 ## 第X章:标题 格式
|
||||
chapter_pattern = r'##\s*第(\d+)章[::]\s*(.+?)\n(.*?)(?=##\s*第|\Z)'
|
||||
matches = re.findall(chapter_pattern, outline_content, re.DOTALL)
|
||||
|
||||
for match in matches:
|
||||
chapter_num = int(match[0])
|
||||
chapter_title = match[1].strip()
|
||||
chapter_content = match[2].strip()
|
||||
|
||||
# 从内容中提取各个部分(简单实现)
|
||||
sections = self.parse_chapter_content(chapter_content)
|
||||
|
||||
# 生成章节
|
||||
content = self.generate(
|
||||
chapter_number=chapter_num,
|
||||
chapter_title=chapter_title,
|
||||
setup=sections.get('承接', ''),
|
||||
development=sections.get('推进', ''),
|
||||
climax=sections.get('高潮', ''),
|
||||
hook=sections.get('钩子', ''),
|
||||
note="从大纲生成"
|
||||
)
|
||||
|
||||
# 保存章节
|
||||
filepath = self.save_chapter(project_path, chapter_num, chapter_title, content)
|
||||
chapters.append(filepath)
|
||||
|
||||
return chapters
|
||||
|
||||
def parse_chapter_content(self, content):
|
||||
"""解析章节内容为各个部分"""
|
||||
sections = {}
|
||||
|
||||
# 简单按行分割
|
||||
current_section = None
|
||||
section_content = []
|
||||
|
||||
for line in content.split('\n'):
|
||||
line = line.strip()
|
||||
|
||||
# 检查是否是新的章节部分
|
||||
if line.startswith('###'):
|
||||
if current_section and section_content:
|
||||
sections[current_section] = '\n'.join(section_content).strip()
|
||||
|
||||
# 提取章节标题
|
||||
section_title = line.replace('###', '').strip()
|
||||
if '承接' in section_title:
|
||||
current_section = '承接'
|
||||
elif '推进' in section_title:
|
||||
current_section = '推进'
|
||||
elif '高潮' in section_title:
|
||||
current_section = '高潮'
|
||||
elif '钩子' in section_title:
|
||||
current_section = '钩子'
|
||||
else:
|
||||
current_section = None
|
||||
|
||||
section_content = []
|
||||
elif current_section and line:
|
||||
section_content.append(line)
|
||||
|
||||
# 处理最后一个部分
|
||||
if current_section and section_content:
|
||||
sections[current_section] = '\n'.join(section_content).strip()
|
||||
|
||||
return sections
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(description="章节生成器")
|
||||
parser.add_argument("--project", help="项目路径", default=".")
|
||||
parser.add_argument("--number", type=int, help="章节编号", required=True)
|
||||
parser.add_argument("--title", help="章节标题", required=True)
|
||||
parser.add_argument("--setup", help="承接部分", default="")
|
||||
parser.add_argument("--development", help="推进部分", default="")
|
||||
parser.add_argument("--climax", help="高潮部分", default="")
|
||||
parser.add_argument("--hook", help="钩子部分", default="")
|
||||
parser.add_argument("--note", help="作者备注", default="")
|
||||
parser.add_argument("--outline", help="从大纲文件生成")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
generator = ChapterGenerator()
|
||||
|
||||
if args.outline:
|
||||
# 从大纲生成
|
||||
chapters = generator.generate_from_outline(args.project, args.outline)
|
||||
print(f"从大纲生成了 {len(chapters)} 个章节")
|
||||
else:
|
||||
# 直接生成
|
||||
content = generator.generate(
|
||||
chapter_number=args.number,
|
||||
chapter_title=args.title,
|
||||
setup=args.setup or "承接内容待补充",
|
||||
development=args.development or "推进内容待补充",
|
||||
climax=args.climax or "高潮内容待补充",
|
||||
hook=args.hook or "钩子内容待补充",
|
||||
note=args.note
|
||||
)
|
||||
|
||||
generator.save_chapter(args.project, args.number, args.title, content)
|
||||
print("章节生成完成")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
287
sync_tools/feishu_sync.py
Executable file
287
sync_tools/feishu_sync.py
Executable file
@ -0,0 +1,287 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
飞书小说同步工具
|
||||
将本地章节同步到飞书文档
|
||||
"""
|
||||
|
||||
import os
|
||||
import json
|
||||
import time
|
||||
from datetime import datetime
|
||||
import requests
|
||||
|
||||
class FeishuNovelSync:
|
||||
def __init__(self, config_path="configs/feishu_config.json"):
|
||||
"""初始化同步工具"""
|
||||
self.config = self.load_config(config_path)
|
||||
self.base_url = "https://open.feishu.cn/open-apis"
|
||||
self.headers = {
|
||||
"Authorization": f"Bearer {self.config.get('access_token')}",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
def load_config(self, config_path):
|
||||
"""加载配置文件"""
|
||||
if os.path.exists(config_path):
|
||||
with open(config_path, 'r', encoding='utf-8') as f:
|
||||
return json.load(f)
|
||||
else:
|
||||
# 默认配置
|
||||
return {
|
||||
"app_id": "",
|
||||
"app_secret": "",
|
||||
"access_token": "",
|
||||
"refresh_token": "",
|
||||
"expire_time": 0
|
||||
}
|
||||
|
||||
def save_config(self, config_path="configs/feishu_config.json"):
|
||||
"""保存配置文件"""
|
||||
os.makedirs(os.path.dirname(config_path), exist_ok=True)
|
||||
with open(config_path, 'w', encoding='utf-8') as f:
|
||||
json.dump(self.config, f, ensure_ascii=False, indent=2)
|
||||
|
||||
def get_access_token(self):
|
||||
"""获取访问令牌"""
|
||||
if self.config.get('expire_time', 0) > time.time():
|
||||
return self.config.get('access_token')
|
||||
|
||||
url = f"{self.base_url}/auth/v3/tenant_access_token/internal"
|
||||
data = {
|
||||
"app_id": self.config['app_id'],
|
||||
"app_secret": self.config['app_secret']
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.post(url, json=data)
|
||||
result = response.json()
|
||||
|
||||
if result.get('code') == 0:
|
||||
self.config['access_token'] = result['tenant_access_token']
|
||||
self.config['expire_time'] = time.time() + result['expire'] - 60
|
||||
self.save_config()
|
||||
return self.config['access_token']
|
||||
else:
|
||||
print(f"获取token失败: {result}")
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"请求失败: {e}")
|
||||
return None
|
||||
|
||||
def create_doc(self, title, folder_token=None):
|
||||
"""创建飞书文档"""
|
||||
token = self.get_access_token()
|
||||
if not token:
|
||||
return None
|
||||
|
||||
url = f"{self.base_url}/drive/v1/files/create"
|
||||
headers = self.headers.copy()
|
||||
headers["Authorization"] = f"Bearer {token}"
|
||||
|
||||
data = {
|
||||
"title": title,
|
||||
"type": "doc",
|
||||
"folder_token": folder_token or ""
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.post(url, headers=headers, json=data)
|
||||
result = response.json()
|
||||
|
||||
if result.get('code') == 0:
|
||||
doc_token = result['data']['token']
|
||||
print(f"文档创建成功: {title} (token: {doc_token})")
|
||||
return doc_token
|
||||
else:
|
||||
print(f"创建文档失败: {result}")
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"创建文档请求失败: {e}")
|
||||
return None
|
||||
|
||||
def update_doc(self, doc_token, content):
|
||||
"""更新文档内容"""
|
||||
token = self.get_access_token()
|
||||
if not token:
|
||||
return False
|
||||
|
||||
url = f"{self.base_url}/drive/v1/files/{doc_token}/content"
|
||||
headers = self.headers.copy()
|
||||
headers["Authorization"] = f"Bearer {token}"
|
||||
|
||||
# 构建文档内容
|
||||
doc_content = {
|
||||
"title": "小说章节",
|
||||
"body": {
|
||||
"blocks": [
|
||||
{
|
||||
"type": "paragraph",
|
||||
"paragraph": {
|
||||
"elements": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": content
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.put(url, headers=headers, json=doc_content)
|
||||
result = response.json()
|
||||
|
||||
if result.get('code') == 0:
|
||||
print(f"文档更新成功: {doc_token}")
|
||||
return True
|
||||
else:
|
||||
print(f"更新文档失败: {result}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"更新文档请求失败: {e}")
|
||||
return False
|
||||
|
||||
def sync_chapter(self, chapter_path, doc_token=None, title=None):
|
||||
"""同步单个章节"""
|
||||
if not os.path.exists(chapter_path):
|
||||
print(f"章节文件不存在: {chapter_path}")
|
||||
return None
|
||||
|
||||
# 读取章节内容
|
||||
with open(chapter_path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
|
||||
# 获取章节标题
|
||||
if not title:
|
||||
title = os.path.basename(chapter_path).replace('.md', '')
|
||||
|
||||
# 如果没有文档token,先创建文档
|
||||
if not doc_token:
|
||||
doc_token = self.create_doc(title)
|
||||
if not doc_token:
|
||||
return None
|
||||
|
||||
# 更新文档内容
|
||||
if self.update_doc(doc_token, content):
|
||||
return doc_token
|
||||
else:
|
||||
return None
|
||||
|
||||
def sync_project(self, project_path, folder_token=None):
|
||||
"""同步整个项目"""
|
||||
if not os.path.exists(project_path):
|
||||
print(f"项目路径不存在: {project_path}")
|
||||
return False
|
||||
|
||||
chapters_dir = os.path.join(project_path, "chapters")
|
||||
if not os.path.exists(chapters_dir):
|
||||
print(f"章节目录不存在: {chapters_dir}")
|
||||
return False
|
||||
|
||||
# 获取所有章节文件
|
||||
chapter_files = []
|
||||
for root, dirs, files in os.walk(chapters_dir):
|
||||
for file in files:
|
||||
if file.endswith('.md'):
|
||||
chapter_files.append(os.path.join(root, file))
|
||||
|
||||
if not chapter_files:
|
||||
print("没有找到章节文件")
|
||||
return False
|
||||
|
||||
print(f"找到 {len(chapter_files)} 个章节文件")
|
||||
|
||||
# 按文件名排序
|
||||
chapter_files.sort()
|
||||
|
||||
# 同步每个章节
|
||||
results = []
|
||||
for i, chapter_file in enumerate(chapter_files, 1):
|
||||
print(f"[{i}/{len(chapter_files)}] 同步: {os.path.basename(chapter_file)}")
|
||||
|
||||
chapter_title = os.path.basename(chapter_file).replace('.md', '')
|
||||
doc_token = self.sync_chapter(chapter_file, None, chapter_title)
|
||||
|
||||
if doc_token:
|
||||
results.append({
|
||||
"file": chapter_file,
|
||||
"title": chapter_title,
|
||||
"doc_token": doc_token,
|
||||
"status": "success"
|
||||
})
|
||||
print(f" 成功: https://example.feishu.cn/docx/{doc_token}")
|
||||
else:
|
||||
results.append({
|
||||
"file": chapter_file,
|
||||
"title": chapter_title,
|
||||
"doc_token": None,
|
||||
"status": "failed"
|
||||
})
|
||||
print(f" 失败")
|
||||
|
||||
# 避免请求过快
|
||||
time.sleep(1)
|
||||
|
||||
# 保存同步结果
|
||||
sync_log = {
|
||||
"project": project_path,
|
||||
"sync_time": datetime.now().isoformat(),
|
||||
"total_chapters": len(chapter_files),
|
||||
"success": len([r for r in results if r['status'] == 'success']),
|
||||
"failed": len([r for r in results if r['status'] == 'failed']),
|
||||
"details": results
|
||||
}
|
||||
|
||||
log_file = os.path.join(project_path, "sync", "sync_log.json")
|
||||
os.makedirs(os.path.dirname(log_file), exist_ok=True)
|
||||
|
||||
with open(log_file, 'w', encoding='utf-8') as f:
|
||||
json.dump(sync_log, f, ensure_ascii=False, indent=2)
|
||||
|
||||
print(f"\n同步完成:")
|
||||
print(f" 总章节: {len(chapter_files)}")
|
||||
print(f" 成功: {sync_log['success']}")
|
||||
print(f" 失败: {sync_log['failed']}")
|
||||
print(f" 日志: {log_file}")
|
||||
|
||||
return sync_log['success'] > 0
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(description="飞书小说同步工具")
|
||||
parser.add_argument("--project", help="项目路径", default=".")
|
||||
parser.add_argument("--config", help="配置文件路径", default="configs/feishu_config.json")
|
||||
parser.add_argument("--chapter", help="单个章节文件路径")
|
||||
parser.add_argument("--init", action="store_true", help="初始化配置")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
sync_tool = FeishuNovelSync(args.config)
|
||||
|
||||
if args.init:
|
||||
# 初始化配置
|
||||
app_id = input("请输入 App ID: ")
|
||||
app_secret = input("请输入 App Secret: ")
|
||||
|
||||
sync_tool.config.update({
|
||||
"app_id": app_id,
|
||||
"app_secret": app_secret
|
||||
})
|
||||
sync_tool.save_config()
|
||||
|
||||
print("配置已保存")
|
||||
return
|
||||
|
||||
if args.chapter:
|
||||
# 同步单个章节
|
||||
sync_tool.sync_chapter(args.chapter)
|
||||
else:
|
||||
# 同步整个项目
|
||||
sync_tool.sync_project(args.project)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
116
templates/番茄黄金三章模板.md
Normal file
116
templates/番茄黄金三章模板.md
Normal file
@ -0,0 +1,116 @@
|
||||
# 番茄黄金三章模板
|
||||
|
||||
## 核心原则
|
||||
1. **第一章300字内出冲突**
|
||||
2. **第一章出现金手指**
|
||||
3. **前三章必须有第一个小爽点打脸**
|
||||
|
||||
## 第一章模板
|
||||
### 结构
|
||||
1. **开篇(50字)**: 吸引注意力,立即进入场景
|
||||
2. **冲突(100字)**: 立即出现矛盾或危机
|
||||
3. **金手指(100字)**: 展现主角的特殊能力或优势
|
||||
4. **初步解决(50字)**: 展现金手指的作用
|
||||
5. **钩子(50字)**: 引出下一章的内容
|
||||
|
||||
### 示例
|
||||
```
|
||||
# 第一章:重生末日七天前
|
||||
|
||||
**开篇**:冰冷的触感从指尖传来,苏晨猛地睁开眼睛。
|
||||
|
||||
**冲突**:手机日期显示:2026年3月30日。七天前。他重生了,回到了末日降临前的七天。
|
||||
|
||||
**金手指**:【叮!末日生存系统激活!当前任务:在七天内囤积足够物资】
|
||||
|
||||
**初步解决**:苏晨看着账户里的五十万存款,笑了。这一世,他要活下来。
|
||||
|
||||
**钩子**:门铃突然响起,快递员送来一个神秘木盒:“苏先生,寄件人说您看到里面的东西就会明白。”
|
||||
```
|
||||
|
||||
## 第二章模板
|
||||
### 结构
|
||||
1. **承接(100字)**: 连接第一章的钩子
|
||||
2. **发展(800字)**: 展开金手指,开始行动
|
||||
3. **小高潮(300字)**: 第一个小爽点
|
||||
4. **新挑战(100字)**: 引出新的问题
|
||||
5. **钩子(100字)**: 为第三章铺垫
|
||||
|
||||
## 第三章模板
|
||||
### 结构
|
||||
1. **承接(100字)**: 解决第二章的挑战
|
||||
2. **推进(600字)**: 主角继续发展
|
||||
3. **第一个爽点(400字)**: 第一次打脸,让读者爽
|
||||
4. **世界观展开(200字)**: 展现更大的世界
|
||||
5. **钩子(100字)**: 引出长期目标
|
||||
|
||||
## 后续章节模板(第4章起)
|
||||
### 标准结构
|
||||
```
|
||||
# 第X章:章节标题
|
||||
|
||||
## 承接(100-300字)
|
||||
连接上一章,平稳过渡
|
||||
|
||||
## 推进(800-2000字)
|
||||
发展情节,展现人物成长
|
||||
|
||||
## 高潮(500-1000字)
|
||||
本章的核心爽点
|
||||
|
||||
## 钩子(100-300字)
|
||||
引出下一章的内容
|
||||
```
|
||||
|
||||
## 字数要求
|
||||
- **每章**: 2500-3500字
|
||||
- **黄金三章**: 每章至少3000字
|
||||
- **对话占比**: >30%(适合听书)
|
||||
- **段落长度**: 每段不超过3行
|
||||
|
||||
## 爽点设计
|
||||
### 常见爽点类型
|
||||
1. **重生先知**: 利用未来信息碾压
|
||||
2. **系统外挂**: 系统任务和奖励
|
||||
3. **家世背景**: 富二代/世家子弟
|
||||
4. **实力碾压**: 等级压制,装备碾压
|
||||
5. **打脸反派**: 反派挑衅→主角打脸
|
||||
|
||||
### 爽点频率
|
||||
- **第一章**: 至少1个爽点
|
||||
- **前三章**: 至少3个爽点
|
||||
- **后续每章**: 至少1-2个爽点
|
||||
|
||||
## 对话技巧
|
||||
### 听书优化
|
||||
1. **多对话**: 对话占比30-50%
|
||||
2. **短对话**: 每句对话不超过2行
|
||||
3. **口语化**: 使用自然的口语表达
|
||||
4. **情绪化**: 通过对话展现人物情绪
|
||||
|
||||
### 对话示例
|
||||
```
|
||||
"就这?"苏鸣歪头,"我还以为多厉害呢。"
|
||||
|
||||
"你……你到底是什么境界?"林浩脸色惨白。
|
||||
|
||||
"比你高一点点。"苏鸣微笑,"大概高一个大境界吧。"
|
||||
```
|
||||
|
||||
## 避坑指南
|
||||
1. **不要直白写设定**: 通过对话和行动展现
|
||||
2. **不要过多心理描写**: 减少内心独白
|
||||
3. **不要拖沓**: 节奏要快,300字内必出进展
|
||||
4. **不要降智反派**: 反派要有合理动机
|
||||
|
||||
## 变现优化
|
||||
### 全勤奖要求
|
||||
- **日更4000字**: 600元 + 5%分成
|
||||
- **日更6000字**: 800元 + 5%分成
|
||||
- **月更10万字**: 听读≥500
|
||||
|
||||
### 听书分成
|
||||
- **对话占比高**: 提升听书体验
|
||||
- **节奏明快**: 减少听书疲劳
|
||||
- **口语化**: 适合语音朗读
|
||||
- **章节分明**: 每章完整故事
|
||||
Loading…
Reference in New Issue
Block a user