1. 背景

最近在把一篇 Markdown 技术文章发布到 DFRobot 论坛时遇到一个实际问题:论坛编辑器不支持 Markdown 输入。

文章里包含标题、表格、列表、代码块、行内代码等结构。如果直接把 Markdown 原文粘贴到论坛,读者看到的是 #|---|、反引号等 Markdown 标记,排版会明显变差。

后来验证发现,该论坛属于 Discuz/Comsenz 系论坛,BBCode 的兼容性更好。例如:

  • 标题可以用 [size][b] 表示;
  • 表格可以用 [table][tr][td] 表示;
  • 代码块可以用 [code]...[/code] 表示;
  • 列表可以用 [list][list=1] 表示。

因此,我把这套转换流程封装成了一个 Codex Skill,后续只要说“把这个 md 转成论坛 BBCode”,就可以复用同一套工具链。

下面是在论坛使用的效果。
在这里插入图片描述

2. Skill 的目标

这个 Skill 的目标不是实现完整的 Markdown 渲染器,而是解决一个很具体的问题:

将常见 Markdown 文档转换成 Discuz/论坛编辑器可接受的保守 BBCode,尽量保留文章结构。

设计时重点考虑以下几类内容:

Markdown 元素 BBCode 输出
# 标题 [size][b]标题[/b][/size]
fenced code block [code]代码[/code]
Markdown 表格 [table][tr][td]
无序列表 [list] + [*]
有序列表 [list=1] + [*]
行内代码 [font=Consolas,monospace]代码[/font]
加粗 [b]文本[/b]

这个范围比较克制,但对技术文章来说已经覆盖了最常见的排版需求。

3. Skill 目录结构

创建后的 Skill 位于:

C:\Users\haili\.codex\skills\markdown-bbcode

目录结构如下:

markdown-bbcode/
├── SKILL.md
├── agents/
│   └── openai.yaml
└── scripts/
    └── md_to_bbcode.py

其中:

  • SKILL.md 描述这个 Skill 何时触发、如何使用、转换规则和验证方式;
  • agents/openai.yaml 提供界面侧展示信息;
  • scripts/md_to_bbcode.py 是实际执行转换的 Python 脚本。

4. SKILL.md 的设计

Skill 的核心是 SKILL.md,它通过 YAML frontmatter 告诉 Codex 这个 Skill 什么时候应该被使用。

关键描述如下:

---
name: markdown-bbcode
description: Convert Markdown documents into forum-friendly BBCode, especially for Discuz/Comsenz forums and editors that do not accept Markdown. Use when a user wants to publish a .md file to a forum while preserving headings, tables, lists, code blocks, inline code, and emphasis.
---

这里的 description 很重要,因为它决定了触发条件。为了提高命中率,描述里明确写了:

  • Markdown documents;
  • forum-friendly BBCode;
  • Discuz/Comsenz forums;
  • editors that do not accept Markdown;
  • headings、tables、lists、code blocks 等结构保留。

这样,当用户说“论坛不支持 Markdown,帮我转成 BBCode”时,Codex 就能较自然地选择这个 Skill。

5. 转换脚本的实现思路

脚本入口支持两个主要参数:

python "C:\Users\haili\.codex\skills\markdown-bbcode\scripts\md_to_bbcode.py" --input "D:\path\article.md"

如果不指定输出路径,默认会在原文件同目录生成:

article.bbcode.txt

也可以手动指定输出路径:

python "C:\Users\haili\.codex\skills\markdown-bbcode\scripts\md_to_bbcode.py" --input "D:\path\article.md" --output "D:\path\article.forum-bbcode.txt"

脚本没有引入第三方依赖,只使用 Python 标准库:

  • argparse 用于解析命令行参数;
  • pathlib 用于处理文件路径;
  • re 用于识别 Markdown 结构。

这样做的好处是可移植性强,不需要额外安装包。

6. 核心状态机

转换过程采用一行一行扫描的方式,并维护几个简单状态:

in_code = False
code_lines = []
table_lines = []
list_kind = None
list_items = []

这些状态分别用于处理:

  • 是否正在代码块中;
  • 当前代码块的内容;
  • 连续表格行;
  • 当前列表类型;
  • 连续列表项。

当遇到空行、标题、普通段落或其他结构时,脚本会先把已经缓存的列表或表格刷新到输出中。这种方式可以避免列表、表格被拆散。

7. 标题转换

Markdown 标题使用正则识别:

heading = re.match(r"^(#{1,6})\s+(.+)$", stripped)

然后根据标题层级生成不同字号:

size = max(3, 7 - level)
out.append(f"[size={size}][b]{inline_bbcode(text)}[/b][/size]\n")

例如:

## 2. Skill 的目标

会转换为:

[size=5][b]2. Skill 的目标[/b][/size]

8. 表格转换

Markdown 表格的行通常长这样:

| 项目 | 参数 |
|---|---|
| 工作电压 | DC 3.6V ~ 5.5V |

脚本先判断一行是否像表格行:

def is_table_row(line: str) -> bool:
    stripped = line.strip()
    return stripped.startswith("|") and stripped.endswith("|") and stripped.count("|") >= 2

再过滤掉分隔行:

def is_table_separator(line: str) -> bool:
    cells = [cell.strip() for cell in line.strip().strip("|").split("|")]
    return bool(cells) and all(re.fullmatch(r":?-{3,}:?", cell) for cell in cells)

最后生成 BBCode:

[table]
[tr]
[td][b]项目[/b][/td]
[td][b]参数[/b][/td]
[/tr]
[tr]
[td]工作电压[/td]
[td]DC 3.6V ~ 5.5V[/td]
[/tr]
[/table]

首行会自动加粗,用来模拟表头。

9. 代码块转换

Markdown 中的 fenced code block 使用三个反引号包裹:

```cpp
Serial.begin(115200);
```

脚本遇到 ```时切换 in_code 状态。代码块内部不再解析 Markdown,而是原样收集,最后输出:

[code]Serial.begin(115200);[/code]

同时,脚本会对方括号做转义,避免代码里的 [] 被论坛误判成 BBCode 标签。

10. 列表转换

连续无序列表:

- 智能灯光人体存在检测
- 房间无人节能控制

会转换成:

[list]
[*] 智能灯光人体存在检测
[*] 房间无人节能控制
[/list]

连续有序列表:

1. 安装库
2. 上传程序

会转换成:

[list=1]
[*] 安装库
[*] 上传程序
[/list]

这里没有保留原始数字,而是交给 Discuz 的有序列表渲染。

11. 行内格式处理

行内格式由 inline_bbcode() 处理:

def inline_bbcode(text: str) -> str:
    text = escape_bbcode(text)
    text = re.sub(r"`([^`]+)`", r"[font=Consolas,monospace]\1[/font]", text)
    text = re.sub(r"\*\*([^*]+)\*\*", r"[b]\1[/b]", text)
    return text

它主要处理三件事:

  1. 转义方括号;
  2. 把行内代码转换成等宽字体;
  3. 把 Markdown 加粗转换成 BBCode 加粗。

例如:

官方示例默认串口波特率为 `115200`。

会转换成:

官方示例默认串口波特率为 [font=Consolas,monospace]115200[/font]。

12. 验证方式

Skill 创建完成后,先运行结构校验:

python "C:\Users\haili\.codex\skills\.system\skill-creator\scripts\quick_validate.py" "C:\Users\haili\.codex\skills\markdown-bbcode"

通过后会输出:

Skill is valid!

然后用真实 Markdown 文档实测:

python "C:\Users\haili\.codex\skills\markdown-bbcode\scripts\md_to_bbcode.py" --input "D:\Test\claudetest\C4002毫米波人体存在传感器接口协议与编程方法.md"

再检查输出中是否包含关键标签:

Select-String -LiteralPath "D:\Test\claudetest\C4002毫米波人体存在传感器接口协议与编程方法.bbcode.txt" -Encoding UTF8 -Pattern '\[table\]|\[code\]|\[list'

如果能看到 [table][code][list] 等标签,说明核心结构已经被转换出来。

13. 遇到的一个编码问题

在 Windows PowerShell 中写入 SKILL.md 时,如果使用某些 UTF-8 写法,文件可能带 BOM。某个校验脚本按系统默认编码读取时,可能出现类似错误:

UnicodeDecodeError: 'gbk' codec can't decode byte

解决方式是把 SKILL.md 写成无 BOM UTF-8:

$enc = New-Object System.Text.UTF8Encoding($false)
[System.IO.File]::WriteAllText("C:\Users\haili\.codex\skills\markdown-bbcode\SKILL.md", $content, $enc)

这个问题和转换逻辑本身无关,但在 Windows 环境下创建 Skill 时很值得注意。

14. 可扩展方向

当前脚本主要覆盖技术文章里最常见的 Markdown 结构。后续可以继续扩展:

  • 支持 Markdown 链接转换为 [url]
  • 支持图片转换为 [img]
  • 支持引用块转换为 [quote]
  • 支持删除线、斜体等更多行内样式;
  • 支持不同论坛的 BBCode 方言;
  • 增加 HTML 富文本输出,方便直接粘贴到富文本编辑器。

不过,对论坛发帖来说,保守转换通常比花哨转换更可靠。尤其是表格和代码块,只要能稳定保留,就已经能显著提升发帖效率。

15. 总结

这个 markdown-bbcode Skill 的价值在于把一次性的“Markdown 转论坛格式”操作沉淀为可复用能力。

它的设计思路比较简单:

  1. SKILL.md 明确触发场景和使用流程;
  2. 用独立 Python 脚本保证转换过程稳定可重复;
  3. 只覆盖高频 Markdown 结构,避免实现过度复杂;
  4. 通过真实论坛稿件验证输出是否可用。

以后再遇到不支持 Markdown 的论坛,只需要把 .md 文件交给这个 Skill,就能快速得到适合粘贴到 BBCode/源码模式的发布稿。

更多推荐