Ariver
2026-08-25 2febc76bbff136bdead74f3bbd52c18197aad8ee
X2.Archived/scripts/sync_0inbox.py
@@ -26,8 +26,11 @@
扫描范围(用户指定)
--------------------
- 顶层黑名单 EXCLUDE_TOP 整棵子树不扫:X2.Archived / 00index / X1.Knomo / X.Attachment / P3.bobo
- 嵌套黑名单 EXCLUDE_PATHS 整棵子树不扫:02DS/02copilot(Copilot 对话日志)、
  02DS/01dril-book(读书笔记库);两者均「永不从其中取内容生成 tagfeed」。
- 仅扫描后缀 SCAN_EXT:.md .markdown .excalidraw .canvas;其它后缀一律跳过
- markdown 同时抽 frontmatter(tags + 关键词字段)与正文 #tag
- markdown 只抽标准 `tags:` frontmatter 与正文 #tag;topic/category/keywords/type 等
  元数据字段绝不当作标签(避免 copilot 对话、读书笔记的元数据被误建页)
幂等 & 安全
----------
@@ -140,15 +143,19 @@
TAG_RE = re.compile(r"(?<![\w/])#([A-Za-z0-9_\u4e00-\u9fff][\w\u4e00-\u9fff/\-]*)")
SCAN_EXT = {".md", ".markdown", ".excalidraw", ".canvas"}
EXCLUDE_TOP = {"X2.Archived", "00index", "X1.Knomo", "X.Attachment", "P3.bobo"}
# 嵌套路径排除(只排除指定子树,不影响其同级目录)。
# 02DS/02copilot:Obsidian Copilot 对话日志,topic: 字段与聊天正文不应被当作标签扫描。
# 02DS/01dril-book:读书笔记库,其正文/元数据亦不希望进入 tagfeed 体系。
# 两者「永不从其中取内容」(与 tagfeed 模板的 excludedPaths 保持一致)。
EXCLUDE_PATHS = {"02DS/02copilot", "02DS/01dril-book"}
INBOX_DIR = "0inbox"
EXCLUDE_DIRS = {".obsidian", ".trash", ".git", ".agents", ".claude",
                ".copilot", ".opencode", ".smart-env", ".workbuddy"}
FM_KEYWORD_KEYS = {
    "aliases", "alias", "keywords", "keyword", "topic", "topics",
    "category", "categories", "subject", "area", "areaOfFocus",
    "people", "person", "project", "projects", "type", "tags",
}
# 只把标准 Obsidian 标签字段 `tags:` 当作标签;不再把 topic/category/subject/
# type/keywords/aliases 等元数据字段误读为标签(否则 copilot 对话的 topic:、
# 读书笔记的 type: 等会被当成标签疯狂建页)。行内 #标签 仍正常扫描。
FM_KEYWORD_KEYS = {"tags"}
def split_fm_value(v) -> list:
@@ -202,11 +209,9 @@
                            tok = tok.strip().lstrip("#")
                            if tok:
                                tags.add(tok)
                for val in fm.values():
                    for tok in split_fm_value(val):
                        mm = TAG_RE.search(str(tok))
                        if mm:
                            tags.add(mm.group(1))
                # 注意:不再遍历所有 frontmatter 值去抽取 #tag。
                # 否则 topic/category/keywords 等元数据字段里出现的 # 文本
                # 会被误判为标签,违背「这类字段不生成 tagfeed 页」的约定。
        in_fence = False
        for line in body.splitlines():
            if line.lstrip().startswith("```"):
@@ -254,7 +259,7 @@
            "> 本页由 DataviewJS 实时计算,笔记有变动时自动刷新。\n\n"
            "```dataviewjs\n"
            "const targetTag = \"__TARGET_TAG__\";\n"
            "const excludedPaths = [\"X2.Archived\", \"X0.Clippings\", \"X1.Knomo\", \"02DS/02copilot\", \".agents\", \".claude\", \".copilot\", \".opencode\", \".smart-env\", \".workbuddy\", \".trash\"];\n"
            "const excludedPaths = [\"X2.Archived\", \"X0.Clippings\", \"X1.Knomo\", \"02DS/02copilot\", \"02DS/01dril-book\", \".agents\", \".claude\", \".copilot\", \".opencode\", \".smart-env\", \".workbuddy\", \".trash\"];\n"
            "dv.paragraph(\"#\" + targetTag + \" 聚合占位\");\n"
            "```\n"
        )
@@ -358,10 +363,15 @@
    all_tags = {}
    for root, dirs, files in os.walk(vault):
        dirs[:] = [d for d in dirs if d not in EXCLUDE_DIRS and not d.startswith(".")]
        top = os.path.relpath(root, vault).split(os.sep)[0]
        rel = os.path.relpath(root, vault)
        top = rel.split(os.sep)[0]
        if top in EXCLUDE_TOP:
            dirs[:] = []
            continue
        # 嵌套路径排除(如 02DS/02copilot)
        if any(rel == p or rel.startswith(p + os.sep) for p in EXCLUDE_PATHS):
            dirs[:] = []
            continue
        for fn in files:
            if fn.startswith("."):
                continue