chore: 今日工作总结复盘-发布流程/Skill/内容规范沉淀
This commit is contained in:
69
skills/docx-publisher/SKILL.md
Normal file
69
skills/docx-publisher/SKILL.md
Normal file
@@ -0,0 +1,69 @@
|
||||
---
|
||||
name: docx-publisher
|
||||
description: 生成图文平台发布版docx文档,支持多平台合规过滤。当用户说"生成发布版"、"生成docx"时触发。工作流:读取drafts/对应草稿 → 注入平台合规规则 → 生成配图 → Node.js脚本生成带图docx → 发给用户。
|
||||
---
|
||||
|
||||
# 图文平台发布版生成器
|
||||
|
||||
将图文草稿(md格式)转换为**发布版 docx文档**,图片直接嵌入,可导入各平台后台直接发布。
|
||||
|
||||
## 触发词
|
||||
|
||||
"生成发布版"、"生成docx"、"发XX平台"
|
||||
|
||||
## 平台合规规则
|
||||
|
||||
详见 `references/平台合规规则.md`。生成前必须读取对应平台的规则。
|
||||
|
||||
## 工作流
|
||||
|
||||
### Step 1:读取草稿 + 合规规则
|
||||
|
||||
1. 读取 `drafts/` 下对应 md 文件(优先读母版 `master` 版,内容更完整)
|
||||
2. 读取 `references/平台合规规则.md`,确认目标平台的**禁用词列表**和**特殊规则**
|
||||
3. 将草稿正文中的禁用词替换为合规替代表述
|
||||
|
||||
### Step 2:生成配图
|
||||
|
||||
根据平台类型决定配图数量:
|
||||
- **图文平台(百家号/搜狐号/公众号等)**:封面图 + 每段落1张,共4-6张
|
||||
- **化工仪器网**:封面图 + 3张段落图(政策/技术/案例)
|
||||
|
||||
**关键**:每张图生成后**立即用 `cp`** 复制到 `published/<日期>_<主题>/assets/` 永久目录,再生成下一张
|
||||
|
||||
### Step 3:生成 docx
|
||||
|
||||
```bash
|
||||
node scripts/gen_docx.js \
|
||||
--title "标题" \
|
||||
--output "output.docx" \
|
||||
--cover ./assets/cover.png \
|
||||
--image2 ./assets/img2.png \
|
||||
--image3 ./assets/img3.png \
|
||||
--image4 ./assets/img4.png \
|
||||
--content-json '<JSON>'
|
||||
```
|
||||
|
||||
### Step 4:发给用户
|
||||
|
||||
发 MEDIA: 路径给用户,告知可直接导入平台发布。
|
||||
|
||||
## docx 格式规范
|
||||
|
||||
| 元素 | 样式 |
|
||||
|------|------|
|
||||
| 标题 | 24pt+,加粗,居中 |
|
||||
| 副标题/摘要 | 灰色,斜体,居中 |
|
||||
| 小标题(H2) | 加粗,段落前后间距 |
|
||||
| 核心观点(加粗句)| 加粗,可带底边线 |
|
||||
| 正文 | 标准字 |
|
||||
| 结束语/来源 | 灰色,小字,居中 |
|
||||
| 图片 | 宽度480-580px,居中 |
|
||||
|
||||
## 禁止出现在发布版的内容
|
||||
|
||||
- 所有平台禁用词(见 `references/平台合规规则.md`)
|
||||
- 联系方式(电话/QQ/微信/网址)
|
||||
- 绝对化用语(最好/第一/国家级/唯一等)
|
||||
- 未通过合规扫描的内容
|
||||
- `[时间] 旁白类型` 格式的分镜指令
|
||||
92
skills/docx-publisher/references/平台合规规则.md
Normal file
92
skills/docx-publisher/references/平台合规规则.md
Normal file
@@ -0,0 +1,92 @@
|
||||
# 平台合规规则
|
||||
|
||||
> 生成各平台发布版 docx 前,必须读取本文件并对目标平台应用对应规则。
|
||||
|
||||
---
|
||||
|
||||
## 化工仪器网 (chem17.com)
|
||||
|
||||
**审核严格程度:高**
|
||||
|
||||
### 禁用词(直接替换,不得出现)
|
||||
|
||||
| 禁用词 | 替代表达 |
|
||||
|--------|---------|
|
||||
| 卓越 | 更高级别 |
|
||||
| 先进 | 高等级 |
|
||||
| 领航 | 最高级别 |
|
||||
| 工信部 | 六部门 |
|
||||
| 首次 | 首次 → "这一次" |
|
||||
| 第一 | 首位/领先 |
|
||||
| 国家级 | 全国性 |
|
||||
| 行业标杆 | 行业示范 |
|
||||
| 保证/确保/100% | 通常可达到/通常情况下 |
|
||||
| 国家级 | 全国性标准 |
|
||||
| 官方 | 权威 |
|
||||
|
||||
### 特殊规则
|
||||
|
||||
- **禁止插入联系方式**:电话/QQ/微信/网址均不可出现
|
||||
- **图片中不得含联系方式水印**
|
||||
- **来源处不得写网址**,只写公司简称
|
||||
- **数据须注明来源**:补贴金额后加"(以各地政策为准)";案例成效加"(模拟参考值,以实际为准)"
|
||||
|
||||
### 替代表达库
|
||||
|
||||
```
|
||||
"详细方案" → "完整方案"
|
||||
"完美解决" → "有效改善"
|
||||
"彻底解决" → "大幅改善"
|
||||
"遥遥领先" → "具备优势"
|
||||
"绝对可靠" → "稳定可靠"
|
||||
"唯一" → "少数具备"
|
||||
"独家" → "自主研发"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 百家号
|
||||
|
||||
**审核严格程度:中**
|
||||
|
||||
- 禁用词参照广告法(参见 `brand/banned-words.md`)
|
||||
- 联系方式:可在文末注明公司名,但不可留具体手机号/微信号
|
||||
- 不得出现竞品负面表述
|
||||
|
||||
---
|
||||
|
||||
## 搜狐号
|
||||
|
||||
**审核严格程度:中**
|
||||
|
||||
- 基本同百家号规则
|
||||
- 图片中不得含水印联系方式
|
||||
|
||||
---
|
||||
|
||||
## 微信公众号
|
||||
|
||||
**审核严格程度:中**
|
||||
|
||||
- 关注自动回复/外部链接需合规
|
||||
- 不得诱导关注/分享
|
||||
- 图片须有版权
|
||||
|
||||
---
|
||||
|
||||
## CSDN / 博客园
|
||||
|
||||
**审核严格程度:低**
|
||||
|
||||
- 技术文章为主,相对宽松
|
||||
- 代码块须格式正确
|
||||
- 可保留技术参考链接
|
||||
|
||||
---
|
||||
|
||||
## 通用规则(所有平台)
|
||||
|
||||
1. **数据必须有来源**:补贴金额注明政策依据;案例数据注明"模拟参考值"
|
||||
2. **禁用绝对化用语**:最好/最佳/第一/唯一/顶级/完美
|
||||
3. **竞品中性原则**:不得出现"碾压/完爆/吊打"等贬低竞品词汇
|
||||
4. **联系方式处理**:原则上不发手机号/微信号,平台要求除外
|
||||
81
skills/docx-publisher/scripts/gen_docx.js
Normal file
81
skills/docx-publisher/scripts/gen_docx.js
Normal file
@@ -0,0 +1,81 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* gen_docx.js
|
||||
* 通用图文平台发布版docx生成器
|
||||
*
|
||||
* 用法:
|
||||
* node gen_docx.js --title "标题" --subtitle "副标题" --output output.docx \
|
||||
* --cover ./assets/cover.png \
|
||||
* --content-json '<JSON_STRING>'
|
||||
*/
|
||||
|
||||
const { Document, Packer, Paragraph, TextRun, ImageRun } = require('/home/node/.openclaw/node_modules/docx');
|
||||
const fs = require('fs');
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
let title = '', subtitle = '', output = '', coverImg = '';
|
||||
let contentJson = '';
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
if (args[i] === '--title' && args[i+1]) title = args[++i];
|
||||
else if (args[i] === '--subtitle' && args[i+1]) subtitle = args[++i];
|
||||
else if (args[i] === '--output' && args[i+1]) output = args[++i];
|
||||
else if (args[i] === '--cover' && args[i+1]) coverImg = args[++i];
|
||||
else if (args[i] === '--content-json' && args[i+1]) contentJson = args[++i];
|
||||
}
|
||||
|
||||
if (!output) { console.error('Usage: --output required'); process.exit(1); }
|
||||
|
||||
const loadImg = (p) => (p && fs.existsSync(p)) ? fs.readFileSync(p) : null;
|
||||
const cover = coverImg ? loadImg(coverImg) : null;
|
||||
|
||||
const E = () => new Paragraph({ text: '' });
|
||||
const TITLE = (t) => new Paragraph({ children: [new TextRun({ text: t, bold: true, size: 48 })], alignment: 'center', spacing: { before: 0, after: 160 } });
|
||||
const SUB = (t) => new Paragraph({ children: [new TextRun({ text: t, size: 24, color: '888888', italics: true })], alignment: 'center', spacing: { before: 0, after: 200 } });
|
||||
const H2 = (t) => new Paragraph({ children: [new TextRun({ text: t, bold: true, size: 36 })], spacing: { before: 320, after: 160 } });
|
||||
const H3 = (t) => new Paragraph({ children: [new TextRun({ text: t, bold: true, size: 28 })], spacing: { before: 200, after: 80 } });
|
||||
const BODY = (t) => new Paragraph({ children: [new TextRun({ text: t, size: 28 })], spacing: { before: 60, after: 80 } });
|
||||
const EMP = (t) => new Paragraph({ children: [new TextRun({ text: t, bold: true, size: 30 })], spacing: { before: 120, after: 100 } });
|
||||
const END = (t) => new Paragraph({ children: [new TextRun({ text: t, size: 22, color: 'AAAAAA', italics: true })], alignment: 'center', spacing: { before: 200, after: 0 } });
|
||||
const IMGP = (d, w, h) => d ? new Paragraph({ children: [new ImageRun({ data: d, transformation: { width: w, height: h }, type: 'png' })], alignment: 'center', spacing: { before: 100, after: 100 } }) : E();
|
||||
|
||||
let config = {};
|
||||
try { config = contentJson ? JSON.parse(contentJson) : {}; } catch(e) { console.error('JSON parse error:', e.message); }
|
||||
|
||||
const children = [];
|
||||
|
||||
// Cover
|
||||
if (cover) { children.push(IMGP(cover, 580, 326)); children.push(E()); }
|
||||
children.push(TITLE(title));
|
||||
if (subtitle) children.push(SUB(subtitle));
|
||||
children.push(E());
|
||||
|
||||
// Sections
|
||||
const sections = config.sections || [];
|
||||
for (const sec of sections) {
|
||||
if (sec.heading) children.push(H2(sec.heading));
|
||||
if (sec.image) children.push(E());
|
||||
if (sec.paragraphs && Array.isArray(sec.paragraphs)) {
|
||||
for (const p of sec.paragraphs) {
|
||||
if (!p || p.trim() === '') { children.push(E()); continue; }
|
||||
const clean = p.trim();
|
||||
if (clean.length < 60 && clean.match(/^[①②③④]/)) {
|
||||
children.push(BODY(clean));
|
||||
} else if (clean.length < 80 && !clean.includes('。')) {
|
||||
children.push(BODY(clean));
|
||||
} else {
|
||||
children.push(BODY(clean));
|
||||
}
|
||||
}
|
||||
}
|
||||
children.push(E());
|
||||
}
|
||||
|
||||
// Source
|
||||
if (config.source) children.push(END(config.source));
|
||||
|
||||
const doc = new Document({ sections: [{ children }] });
|
||||
Packer.toBuffer(doc).then(buf => {
|
||||
fs.writeFileSync(output, buf);
|
||||
console.log('done');
|
||||
}).catch(e => { console.error(e); process.exit(1); });
|
||||
Reference in New Issue
Block a user