@openschool_01/skills 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bundled-skills/human-writing/package.json +12 -0
- package/bundled-skills/human-writing/skill/OPENSCHOOL-META.md +7 -0
- package/bundled-skills/human-writing/skill/SKILL.md +57 -0
- package/bundled-skills/human-writing/skill/references/rewrite-rules.md +22 -0
- package/bundled-skills/nano-banana-pro/package.json +12 -0
- package/bundled-skills/nano-banana-pro/skill/OPENSCHOOL-META.md +7 -0
- package/bundled-skills/nano-banana-pro/skill/SKILL.md +62 -0
- package/bundled-skills/nano-banana-pro/skill/references/prompt-rules.md +22 -0
- package/bundled-skills/wechat-assistant/skill/OPENSCHOOL-META.md +11 -10
- package/bundled-skills/wechat-assistant/skill/SKILL.md +70 -66
- package/bundled-skills/wechat-assistant/skill/config/wechat-publish-api.example.json +6 -0
- package/bundled-skills/wechat-assistant/skill/scripts/__pycache__/render_wechat_api_content.cpython-314.pyc +0 -0
- package/bundled-skills/wechat-assistant/skill/scripts/render_wechat_api_content.py +191 -0
- package/bundled-skills/wechat-assistant/skill/scripts/render_wechat_html.py +203 -139
- package/bundled-skills/wechat-assistant/skill/scripts/wechat_html_api_publish.py +288 -0
- package/package.json +1 -1
- package/registry/skills-registry.json +64 -2
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Human Writing
|
|
2
|
+
|
|
3
|
+
适用于把 AI 生成的生硬文案,改成更像真人写的版本。
|
|
4
|
+
|
|
5
|
+
当前版本专注 4 件事:
|
|
6
|
+
|
|
7
|
+
1. 减少模板味和说明书腔
|
|
8
|
+
2. 增加真实判断和自然转场
|
|
9
|
+
3. 保留原信息,但让表达更顺
|
|
10
|
+
4. 让文案更适合内容平台直接使用
|
|
11
|
+
|
|
12
|
+
## 什么时候用
|
|
13
|
+
|
|
14
|
+
当用户要做这些事时使用:
|
|
15
|
+
|
|
16
|
+
- 让 AI 文案更像真人写的
|
|
17
|
+
- 给公众号文章去掉 AI 味
|
|
18
|
+
- 给小红书文案去掉模板感
|
|
19
|
+
- 把结构保留但把语气写活
|
|
20
|
+
|
|
21
|
+
## 推荐工作流
|
|
22
|
+
|
|
23
|
+
### 1. 先判断目标平台
|
|
24
|
+
|
|
25
|
+
优先确认:
|
|
26
|
+
|
|
27
|
+
- 是公众号
|
|
28
|
+
- 小红书
|
|
29
|
+
- 视频口播
|
|
30
|
+
- 普通说明文
|
|
31
|
+
|
|
32
|
+
### 2. 再判断要保留什么
|
|
33
|
+
|
|
34
|
+
优先保留:
|
|
35
|
+
|
|
36
|
+
- 核心结论
|
|
37
|
+
- 关键信息
|
|
38
|
+
- 必要步骤
|
|
39
|
+
|
|
40
|
+
优先改掉:
|
|
41
|
+
|
|
42
|
+
- 套话
|
|
43
|
+
- 重复解释
|
|
44
|
+
- 机械转场
|
|
45
|
+
- 空泛词
|
|
46
|
+
|
|
47
|
+
### 3. 输出时默认给两版
|
|
48
|
+
|
|
49
|
+
- 精修版
|
|
50
|
+
- 更口语一点的版本
|
|
51
|
+
|
|
52
|
+
## 输出要求
|
|
53
|
+
|
|
54
|
+
如果用户没有特别说明,默认补一段简短说明:
|
|
55
|
+
|
|
56
|
+
- 这次主要改掉了什么
|
|
57
|
+
- 哪一版更适合当前平台
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Nano Banana Pro
|
|
2
|
+
|
|
3
|
+
适用于把一段需求整理成更适合生图模型执行的成品提示词。
|
|
4
|
+
|
|
5
|
+
当前版本专注 4 件事:
|
|
6
|
+
|
|
7
|
+
1. 把模糊需求补成完整画面描述
|
|
8
|
+
2. 统一画风、构图、比例和细节层级
|
|
9
|
+
3. 生成主提示词、备选提示词和负面提示词
|
|
10
|
+
4. 让图片更适合海报、封面、宣传图这类场景
|
|
11
|
+
|
|
12
|
+
当前版本不负责:
|
|
13
|
+
|
|
14
|
+
- 直接调用生图网站
|
|
15
|
+
- 自动下载图片
|
|
16
|
+
- 自动排版进公众号或小红书
|
|
17
|
+
|
|
18
|
+
## 什么时候用
|
|
19
|
+
|
|
20
|
+
当用户要做这些事时使用:
|
|
21
|
+
|
|
22
|
+
- 生成海报图提示词
|
|
23
|
+
- 生成封面图提示词
|
|
24
|
+
- 生成产品宣传图提示词
|
|
25
|
+
- 优化一段生图需求
|
|
26
|
+
|
|
27
|
+
## 推荐工作流
|
|
28
|
+
|
|
29
|
+
### 1. 先明确用途
|
|
30
|
+
|
|
31
|
+
至少确认:
|
|
32
|
+
|
|
33
|
+
- 这张图要用在哪里
|
|
34
|
+
- 主要对象是什么
|
|
35
|
+
- 希望什么风格
|
|
36
|
+
- 想强调什么信息
|
|
37
|
+
|
|
38
|
+
### 2. 再补全关键维度
|
|
39
|
+
|
|
40
|
+
默认补齐这些信息:
|
|
41
|
+
|
|
42
|
+
- 画面主体
|
|
43
|
+
- 构图方式
|
|
44
|
+
- 色彩方向
|
|
45
|
+
- 比例
|
|
46
|
+
- 背景与氛围
|
|
47
|
+
- 细节限制
|
|
48
|
+
|
|
49
|
+
### 3. 默认输出三部分
|
|
50
|
+
|
|
51
|
+
- 主提示词
|
|
52
|
+
- 2 到 3 条可选变体
|
|
53
|
+
- 负面提示词
|
|
54
|
+
|
|
55
|
+
## 输出要求
|
|
56
|
+
|
|
57
|
+
如果用户没指定,默认一起给:
|
|
58
|
+
|
|
59
|
+
- 中文主提示词
|
|
60
|
+
- 英文高质量版提示词
|
|
61
|
+
- 负面提示词
|
|
62
|
+
- 一句使用建议
|
|
@@ -2,18 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
- 收录名称:公众号助手
|
|
4
4
|
- 收录方式:OpenSchool 严选整合
|
|
5
|
-
-
|
|
6
|
-
|
|
7
|
-
当前版本整合了公众号写作流程里最常用的三块能力:
|
|
5
|
+
- 当前版本:内容与草稿箱双模版
|
|
8
6
|
|
|
7
|
+
本版本重点能力:
|
|
9
8
|
1. 标题方向整理
|
|
10
|
-
2.
|
|
11
|
-
3.
|
|
12
|
-
|
|
13
|
-
本版本刻意不包含:
|
|
9
|
+
2. 正文写作与质量检查
|
|
10
|
+
3. 公众号预览 HTML 渲染(默认无尾注)
|
|
11
|
+
4. 可选上传草稿箱(用户显式提供 appid/appsecret 时执行)
|
|
14
12
|
|
|
13
|
+
默认不包含:
|
|
15
14
|
- 生图
|
|
16
|
-
-
|
|
17
|
-
-
|
|
15
|
+
- 封面自动生成
|
|
16
|
+
- 自动正式发布
|
|
18
17
|
|
|
19
|
-
|
|
18
|
+
上线约束:
|
|
19
|
+
- 不内置任何个人信息尾注
|
|
20
|
+
- 不在仓库存储真实公众号凭据
|
|
@@ -2,108 +2,112 @@
|
|
|
2
2
|
|
|
3
3
|
适用于把一个选题整理成可发布到微信公众号的完整内容稿。
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
5
|
+
当前版本重点做 5 件事:
|
|
6
|
+
1. 选题和标题方向整理
|
|
7
|
+
2. 正文 Markdown 写作
|
|
8
|
+
3. 微信预览 HTML 渲染
|
|
9
|
+
4. 文章质量检查
|
|
10
|
+
5. 可选上传到公众号草稿箱(需要用户主动提供凭据)
|
|
11
|
+
|
|
12
|
+
默认不做:
|
|
14
13
|
- 生图
|
|
15
|
-
-
|
|
16
|
-
-
|
|
14
|
+
- 封面图自动生成
|
|
15
|
+
- 自动正式发布
|
|
17
16
|
|
|
18
|
-
|
|
17
|
+
默认不追加任何个人尾注信息。
|
|
19
18
|
|
|
20
|
-
|
|
19
|
+
## 什么时候用
|
|
21
20
|
|
|
22
21
|
- 写公众号文章
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
22
|
+
- 把直播内容整理成公众号稿件
|
|
23
|
+
- 生成公众号标题候选
|
|
24
|
+
- 输出可预览、可复制到后台的 HTML
|
|
25
|
+
- 需要通过 API 上传到草稿箱
|
|
27
26
|
|
|
28
|
-
##
|
|
27
|
+
## 推荐流程
|
|
29
28
|
|
|
30
|
-
### 1.
|
|
29
|
+
### 1. 收集输入
|
|
31
30
|
|
|
32
31
|
至少确认这 4 个信息:
|
|
33
|
-
|
|
34
32
|
- 文章主题
|
|
35
33
|
- 目标读者
|
|
36
|
-
-
|
|
34
|
+
- 读者当前最常见问题
|
|
37
35
|
- 这篇文章承诺解决什么
|
|
38
36
|
|
|
39
|
-
### 2.
|
|
40
|
-
|
|
41
|
-
优先输出 3 到 5 个标题,并解释它们分别适合:
|
|
42
|
-
|
|
43
|
-
- 偏方法拆解
|
|
44
|
-
- 偏观点判断
|
|
45
|
-
- 偏案例复盘
|
|
37
|
+
### 2. 先出标题方向
|
|
46
38
|
|
|
47
|
-
|
|
39
|
+
输出 3 到 5 个标题,并说明适用场景。
|
|
48
40
|
|
|
49
|
-
|
|
41
|
+
### 3. 再写正文 Markdown
|
|
50
42
|
|
|
51
|
-
|
|
43
|
+
正文建议:
|
|
44
|
+
- 4 到 6 个二级标题
|
|
45
|
+
- 先给判断,再解释
|
|
46
|
+
- 至少 1 个真实场景
|
|
47
|
+
- 至少 1 个常见误区
|
|
48
|
+
- 至少 1 组可执行动作
|
|
52
49
|
|
|
53
|
-
|
|
50
|
+
### 4. 渲染公众号 HTML(无尾注)
|
|
54
51
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
- 至少包含 1 个常见误区
|
|
59
|
-
- 至少包含 1 组可执行动作
|
|
60
|
-
- 结尾自然引向下一篇或下一步动作
|
|
52
|
+
```bash
|
|
53
|
+
python scripts/render_wechat_html.py --input "<article.md>" --output "<article>.html"
|
|
54
|
+
```
|
|
61
55
|
|
|
62
|
-
|
|
56
|
+
### 5. 质量检查(可选)
|
|
63
57
|
|
|
64
|
-
|
|
58
|
+
```bash
|
|
59
|
+
python scripts/audit_article_quality.py --input "<article.md>"
|
|
60
|
+
```
|
|
65
61
|
|
|
66
|
-
###
|
|
62
|
+
### 6. 上传草稿箱(可选)
|
|
67
63
|
|
|
68
|
-
|
|
64
|
+
当用户明确需要上传草稿箱时,使用:
|
|
69
65
|
|
|
70
66
|
```bash
|
|
71
|
-
python scripts/
|
|
67
|
+
python scripts/wechat_html_api_publish.py \
|
|
68
|
+
--markdown "<article.md>" \
|
|
69
|
+
--config "config/wechat-publish-api.example.json" \
|
|
70
|
+
--dry-run
|
|
72
71
|
```
|
|
73
72
|
|
|
74
|
-
|
|
73
|
+
确认 dry-run 输出没问题后,再去掉 `--dry-run`。
|
|
75
74
|
|
|
76
|
-
|
|
75
|
+
也可以直接传入账号参数:
|
|
77
76
|
|
|
78
77
|
```bash
|
|
79
|
-
python scripts/
|
|
78
|
+
python scripts/wechat_html_api_publish.py \
|
|
79
|
+
--markdown "<article.md>" \
|
|
80
|
+
--appid "<appid>" \
|
|
81
|
+
--appsecret "<appsecret>" \
|
|
82
|
+
--author "<author>"
|
|
80
83
|
```
|
|
81
84
|
|
|
85
|
+
## 安全约束
|
|
86
|
+
|
|
87
|
+
- 不要把用户的 appid/appsecret 写死进脚本。
|
|
88
|
+
- 不要把真实凭据提交到仓库。
|
|
89
|
+
- 默认使用示例配置文件,用户自行填写本地私密配置。
|
|
90
|
+
|
|
82
91
|
## 目录说明
|
|
83
92
|
|
|
84
|
-
- `references/title-guidelines.md
|
|
85
|
-
|
|
86
|
-
- `
|
|
87
|
-
|
|
88
|
-
- `
|
|
89
|
-
|
|
90
|
-
- `
|
|
91
|
-
|
|
92
|
-
- `scripts/
|
|
93
|
-
Markdown 转微信 HTML
|
|
94
|
-
- `scripts/audit_article_quality.py`
|
|
95
|
-
质量检查脚本
|
|
93
|
+
- `references/title-guidelines.md`:标题参考
|
|
94
|
+
- `references/writing-rules.md`:正文写作规则
|
|
95
|
+
- `assets/wechat-article-template.html`:公众号预览模板
|
|
96
|
+
- `config/wechat-style-config.json`:样式配置
|
|
97
|
+
- `config/wechat-publish-api.example.json`:草稿箱上传配置示例
|
|
98
|
+
- `scripts/render_wechat_html.py`:Markdown 转公众号预览 HTML(无尾注)
|
|
99
|
+
- `scripts/render_wechat_api_content.py`:Markdown 转 API 友好内容片段
|
|
100
|
+
- `scripts/wechat_html_api_publish.py`:调用公众号接口创建草稿
|
|
101
|
+
- `scripts/audit_article_quality.py`:质量检查
|
|
96
102
|
|
|
97
103
|
## 交付要求
|
|
98
104
|
|
|
99
105
|
默认交付:
|
|
100
|
-
|
|
101
|
-
- 3 到 5 个标题备选
|
|
106
|
+
- 3 到 5 个标题候选
|
|
102
107
|
- 1 篇 Markdown 正文
|
|
103
|
-
- 1 个 HTML
|
|
104
|
-
|
|
105
|
-
如果脚本已经执行,还要明确告诉用户:
|
|
108
|
+
- 1 个 HTML 预览文件
|
|
106
109
|
|
|
107
|
-
|
|
108
|
-
-
|
|
109
|
-
-
|
|
110
|
+
如果执行了草稿箱上传,还要明确返回:
|
|
111
|
+
- 是否 dry-run
|
|
112
|
+
- 草稿接口返回结果
|
|
113
|
+
- 产物文件路径
|
|
Binary file
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Render markdown into WeChat Official Account API-friendly HTML fragment."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
import html
|
|
8
|
+
import re
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
WECHAT_INLINE = {
|
|
12
|
+
"p": "margin:0 0 18px;line-height:1.85em;font-size:16px;color:#222;text-align:justify;word-break:break-word;",
|
|
13
|
+
"h2": "margin:30px 0 14px;line-height:1.75em;font-size:17px;font-weight:700;color:#0f6fff;",
|
|
14
|
+
"h3": "margin:22px 0 12px;line-height:1.75em;font-size:15px;font-weight:700;color:#0f6fff;",
|
|
15
|
+
"li": "margin:0 0 10px;line-height:1.85em;font-size:16px;color:#222;",
|
|
16
|
+
"blockquote": "margin:0 0 18px;padding:10px 14px;background:#f7faff;border-left:3px solid #0f6fff;color:#355070;line-height:1.85em;font-size:15px;",
|
|
17
|
+
"pre": "margin:0 0 18px;padding:12px 14px;background:#f7f8fa;border:0;border-left:3px solid rgba(15,111,255,.45);overflow-x:auto;",
|
|
18
|
+
"code": "font-size:14px;line-height:1.75em;color:#333;font-family:Consolas,Menlo,monospace;white-space:pre-wrap;",
|
|
19
|
+
"sep": "margin:22px 0 20px;text-align:center;color:#bbb;letter-spacing:1px;",
|
|
20
|
+
"cta": "margin-top:28px;padding-top:14px;border-top:1px solid #e6e6e6;",
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def convert_inline(text: str) -> str:
|
|
25
|
+
text = html.escape(text)
|
|
26
|
+
text = re.sub(r"`([^`]+)`", r"<code style=\"%s\">\1</code>" % WECHAT_INLINE["code"], text)
|
|
27
|
+
text = re.sub(r"\*\*([^*]+)\*\*", r"<strong style=\"color:#0f6fff;font-weight:700;\">\1</strong>", text)
|
|
28
|
+
text = re.sub(r"\*([^*]+)\*", r"<em>\1</em>", text)
|
|
29
|
+
text = re.sub(r"\[([^\]]+)\]\(([^)]+)\)", r'<a href="\2">\1</a>', text)
|
|
30
|
+
return text
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def markdown_to_api_blocks(content: str):
|
|
34
|
+
lines = content.splitlines()
|
|
35
|
+
title = ""
|
|
36
|
+
blocks = []
|
|
37
|
+
in_code = False
|
|
38
|
+
code = []
|
|
39
|
+
para = []
|
|
40
|
+
quote = []
|
|
41
|
+
list_items: list[str] = []
|
|
42
|
+
list_kind = ""
|
|
43
|
+
has_cta = False
|
|
44
|
+
|
|
45
|
+
def flush_para():
|
|
46
|
+
nonlocal para, has_cta
|
|
47
|
+
if not para:
|
|
48
|
+
return
|
|
49
|
+
txt = re.sub(r"\s+", " ", " ".join(p.strip() for p in para if p.strip())).strip()
|
|
50
|
+
para = []
|
|
51
|
+
if not txt:
|
|
52
|
+
return
|
|
53
|
+
if any(k in txt for k in ["留言", "评论", "关键词", "下一篇", "下篇", "领取"]):
|
|
54
|
+
has_cta = True
|
|
55
|
+
blocks.append(f'<p style="{WECHAT_INLINE["p"]}">{convert_inline(txt)}</p>')
|
|
56
|
+
|
|
57
|
+
def flush_quote():
|
|
58
|
+
nonlocal quote
|
|
59
|
+
if not quote:
|
|
60
|
+
return
|
|
61
|
+
txt = re.sub(r"\s+", " ", " ".join(q.strip() for q in quote if q.strip())).strip()
|
|
62
|
+
quote = []
|
|
63
|
+
if not txt:
|
|
64
|
+
return
|
|
65
|
+
blocks.append(f'<blockquote style="{WECHAT_INLINE["blockquote"]}">{convert_inline(txt)}</blockquote>')
|
|
66
|
+
|
|
67
|
+
def flush_list():
|
|
68
|
+
nonlocal list_items, list_kind
|
|
69
|
+
if not list_items:
|
|
70
|
+
return
|
|
71
|
+
tag = "ol" if list_kind == "ol" else "ul"
|
|
72
|
+
items = "".join(f'<li style="{WECHAT_INLINE["li"]}">{convert_inline(item)}</li>' for item in list_items)
|
|
73
|
+
blocks.append(f"<{tag}>{items}</{tag}>")
|
|
74
|
+
list_items = []
|
|
75
|
+
list_kind = ""
|
|
76
|
+
|
|
77
|
+
for raw in lines:
|
|
78
|
+
s = raw.rstrip()
|
|
79
|
+
st = s.strip()
|
|
80
|
+
if st.startswith("```"):
|
|
81
|
+
flush_para()
|
|
82
|
+
flush_quote()
|
|
83
|
+
flush_list()
|
|
84
|
+
if in_code:
|
|
85
|
+
code_html = html.escape("\n".join(code))
|
|
86
|
+
blocks.append(f'<pre style="{WECHAT_INLINE["pre"]}"><code style="{WECHAT_INLINE["code"]}">{code_html}</code></pre>')
|
|
87
|
+
code = []
|
|
88
|
+
in_code = False
|
|
89
|
+
else:
|
|
90
|
+
in_code = True
|
|
91
|
+
continue
|
|
92
|
+
|
|
93
|
+
if in_code:
|
|
94
|
+
code.append(s)
|
|
95
|
+
continue
|
|
96
|
+
|
|
97
|
+
if not st:
|
|
98
|
+
flush_para()
|
|
99
|
+
flush_quote()
|
|
100
|
+
flush_list()
|
|
101
|
+
continue
|
|
102
|
+
|
|
103
|
+
if not title and st.startswith("# "):
|
|
104
|
+
flush_para()
|
|
105
|
+
flush_quote()
|
|
106
|
+
flush_list()
|
|
107
|
+
title = st[2:].strip()
|
|
108
|
+
continue
|
|
109
|
+
|
|
110
|
+
if st.startswith("## "):
|
|
111
|
+
flush_para()
|
|
112
|
+
flush_quote()
|
|
113
|
+
flush_list()
|
|
114
|
+
blocks.append(f'<h2 style="{WECHAT_INLINE["h2"]}">{convert_inline(st[3:])}</h2>')
|
|
115
|
+
continue
|
|
116
|
+
|
|
117
|
+
if st.startswith("### "):
|
|
118
|
+
flush_para()
|
|
119
|
+
flush_quote()
|
|
120
|
+
flush_list()
|
|
121
|
+
blocks.append(f'<h3 style="{WECHAT_INLINE["h3"]}">{convert_inline(st[4:])}</h3>')
|
|
122
|
+
continue
|
|
123
|
+
|
|
124
|
+
if st.startswith("> "):
|
|
125
|
+
flush_para()
|
|
126
|
+
flush_list()
|
|
127
|
+
quote.append(st[2:])
|
|
128
|
+
continue
|
|
129
|
+
|
|
130
|
+
if re.match(r"^\d+\.\s+", st):
|
|
131
|
+
flush_para()
|
|
132
|
+
flush_quote()
|
|
133
|
+
kind = "ol"
|
|
134
|
+
if list_kind and list_kind != kind:
|
|
135
|
+
flush_list()
|
|
136
|
+
list_kind = kind
|
|
137
|
+
list_items.append(re.sub(r"^\d+\.\s+", "", st))
|
|
138
|
+
continue
|
|
139
|
+
|
|
140
|
+
if re.match(r"^[-*]\s+", st):
|
|
141
|
+
flush_para()
|
|
142
|
+
flush_quote()
|
|
143
|
+
kind = "ul"
|
|
144
|
+
if list_kind and list_kind != kind:
|
|
145
|
+
flush_list()
|
|
146
|
+
list_kind = kind
|
|
147
|
+
list_items.append(re.sub(r"^[-*]\s+", "", st))
|
|
148
|
+
continue
|
|
149
|
+
|
|
150
|
+
if st == "---":
|
|
151
|
+
flush_para()
|
|
152
|
+
flush_quote()
|
|
153
|
+
flush_list()
|
|
154
|
+
blocks.append(f'<p style="{WECHAT_INLINE["sep"]}">———</p>')
|
|
155
|
+
continue
|
|
156
|
+
|
|
157
|
+
para.append(st)
|
|
158
|
+
|
|
159
|
+
flush_para()
|
|
160
|
+
flush_quote()
|
|
161
|
+
flush_list()
|
|
162
|
+
return title, "\n".join(blocks), has_cta
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def render_api_content(markdown: str, title_override: str = "") -> tuple[str, str, bool]:
|
|
166
|
+
title, body, has_cta = markdown_to_api_blocks(markdown)
|
|
167
|
+
title = title_override or title or "公众号文章"
|
|
168
|
+
cta = (
|
|
169
|
+
""
|
|
170
|
+
if has_cta
|
|
171
|
+
else f'<section style="{WECHAT_INLINE["cta"]}"><p style="{WECHAT_INLINE["p"]}color:#355070;">如果这篇内容对你有帮助,欢迎留言关键词,我会继续补充相关资料。</p></section>'
|
|
172
|
+
)
|
|
173
|
+
return title, body + cta, has_cta
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def main() -> None:
|
|
177
|
+
parser = argparse.ArgumentParser()
|
|
178
|
+
parser.add_argument("--input", required=True)
|
|
179
|
+
parser.add_argument("--output", required=True)
|
|
180
|
+
parser.add_argument("--title", default="")
|
|
181
|
+
args = parser.parse_args()
|
|
182
|
+
|
|
183
|
+
markdown = Path(args.input).read_text(encoding="utf-8")
|
|
184
|
+
_, html_doc, _ = render_api_content(markdown, args.title)
|
|
185
|
+
out = Path(args.output)
|
|
186
|
+
out.parent.mkdir(parents=True, exist_ok=True)
|
|
187
|
+
out.write_text(html_doc, encoding="utf-8")
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
if __name__ == "__main__":
|
|
191
|
+
main()
|