Article Archiver
自动抓取网页文章和 Twitter Article,完整归档到飞书文档。
⚠️ 关键实现要点(必读)
1. 必须读取配置文件获取归档位置:
CODEBLOCK0
2. 图片处理流程:
- - 使用
agent-browser 打开页面 - 滚动加载所有图片(懒加载)
- 用
img.preview 或类似选择器过滤内容图片(排除头像、图标) - 分析每张图片在文章中的位置(找到前面最近的段落文本)
- 下载图片到 INLINECODE2
- 使用
feishu_doc upload_image 并指定 after_block_id 插入到正确位置 - 关键:
upload_image 支持 after_block_id 参数,可以精确控制图片位置
3. 样式保留:
- - 引用块:使用
> 开头 - 粗体:使用 INLINECODE8
- 列表:使用
- 或 INLINECODE10 - 代码块:使用
` INLINECODE12
4. 内容传递方式:
- - 使用 OpenClaw 的
read 工具读取文件 - 不能在 feishu_doc 参数中使用 INLINECODE14
- 大文件分批处理(每批 100 行)
5. 去重检查:
- - 归档前先搜索同名文档
- 如果存在,返回已有文档链接
- 不要重复创建
6. 防止乱码和格式问题:
- 使用
html-to-markdown-fixed.js(而不是
html-to-markdown-final.js)
- 该脚本使用 Buffer 确保 UTF-8 编码正确,避免中文字符被截断
- 自动移除替换字符
\ufffd 和
���
- 脚本会自动修复
# \n\n内容 格式为
# 内容
- 避免飞书渲染时标题为空、内容在下一行的问题
- 写入飞书前,检查 JSON 输出中是否有
\ufffd 或
���
- 检查标题格式是否正确(标题标记和内容在同一行)
- 不要手动重写原文内容,必须从原始来源精确提取
- 长文本分段写入时,不要在多字节字符中间截断
7. 避免重复标题:
- - 飞书文档已经有页面标题(Page title),不要在正文开头再写一次标题
- 从原文提取内容时,跳过第一个 H1 标题(通常就是文章标题)
- 如果 web_fetch 或 agent-browser 提取的内容以标题开头,删除第一行
- 正文应该从引言、摘要或第一段正文开始
- 元数据后直接接正文,不要插入标题
8. 元数据格式(重要):
- - 必须使用引用块格式(灰色背景,有样式,好看)
- 标准格式(参考 https://qingzhao.feishu.cn/wiki/ZVCFwN7bci1uyhknLpucA18FnSe):
> **原始链接:** https://example.com/article
> **归档时间:** 2026-03-12 22:16:00
> **来源:** example.com
> **作者:** 张三 (@username)
---
- 每行以
> 开头(引用块)
- 字段名用
**粗体** + 冒号
- 字段值用普通文本
- 元数据和正文之间用分隔线
--- 分开
- Markdown 表格(会被渲染成飞书表格,不美观)
- 普通文本(没有样式,不好看)
- Emoji(📄、📅、✍️)在引用块中显示不好
9. 图片位置精确控制:
- - 图片必须插入到正确的段落之后,不能随意放置
- 使用
feishu_doc upload_image 时,必须指定 INLINECODE27 - 步骤:
1. 先写入全部正文内容
2. 使用
feishu_doc list_blocks 获取所有 block_id
3. 根据图片前面的文本内容,找到对应的 block_id
4. 用
after_block_id 参数将图片插入到正确位置
- - 不要在写入正文时就尝试插入图片(会导致位置错乱)
核心功能
- - ✅ 支持长文章(600+ 行)
- ✅ 支持多图片(20+ 张),图片插入到正确位置
- ✅ 保留完整格式(粗体、代码块、列表)
- ✅ 按月份自动组织
- ✅ 自动去重(检测重复 URL)
使用方法
触发条件
自动触发(无需用户明确说"归档"):
- - 用户发送 Twitter/X 链接:
https://x.com/... 或 INLINECODE31 - 用户发送微信公众号链接: INLINECODE32
- 用户说"保存 [链接]"、"记录 [链接]"、"收藏 [链接]"
手动触发:
- - 用户明确说"归档这篇文章"、"保存到飞书"
- 用户发送其他网页链接并询问是否归档
触发后的行为:
- 1. 立即开始归档,不要询问确认
- 归档过程中可以简短提示"正在归档..."
- 归档成功后发送通知消息(包含文档链接)
- 归档失败时说明原因,提供解决建议
归档流程
0. 去重检查(必须先执行)
在开始归档前,必须先检查文档是否已存在:
CODEBLOCK2
去重规则:
- - 标题完全一样 → 视为已存在
- 跳过归档,返回已存在文档的链接
- 不要重复创建相同标题的文档
实现方式:
- 1. 先提取文章标题(不需要完整内容)
- 在目标位置搜索同名文档
- 如果存在,直接返回链接并跳过
- 如果不存在,继续正常归档流程
1. 识别文章类型
CODEBLOCK3
2. 抓取内容
Twitter Article(长文章 + 多图片):
CODEBLOCK4
普通网页:
CODEBLOCK5
3. 分段写入(核心方法)
⚠️ 重要:正确的文件内容传递方式
在 OpenClaw 会话中,不能使用 bash 命令替换 $(cat file) 直接传给 feishu_doc,因为会被当成字面字符串。
正确方法:
方法 1:使用 OpenClaw 的 read 工具(推荐)
CODEBLOCK6
方法 2:分批读取和上传(适合大文件)
CODEBLOCK7
图片处理(关键步骤):
CODEBLOCK8
图片上传注意事项:
- - 使用
feishu_doc 的 upload_image action - 参数名是
file_path,不是 INLINECODE37 - 每次上传后延迟 300ms 避免限流
- 图片会自动插入到文档末尾
样式保留:
- - 使用 Markdown 格式保留样式
- 引用块:使用
> 开头 - 代码块:使用
` 包裹 - 列表:使用
- 或 1. 开头 - 粗体:使用 INLINECODE43
关键原则:
- - 必须先读取文件内容到变量
- 然后把变量传给 feishudoc
- 不能在 feishudoc 参数中使用 bash 命令替换
- 大文件分批处理(每批 100 ���)
- 分批之间延迟 0.3 秒
- 图片单独上传,不要嵌入 Markdown
4. 添加元数据头部
标准格式(引用块 + 粗体字段名,参考 https://qingzhao.feishu.cn/wiki/ZVCFwN7bci1uyhknLpucA18FnSe):
CODEBLOCK9
关键点:
- - 每行以
> 开头(引用块) - 字段名用
**粗体** + 冒号 - 字段值用普通文本
- 不要用 Emoji(📄、📅、✍️)
- 元数据和正文之间用分隔线
--- 分开
5. 写入正文内容
使用 OpenClaw read 工具读取并写入:
CODEBLOCK10
飞书会自动处理:
- - Markdown 图片语法
 → 自动下载并转换为内部图片 - 代码块
`language ... ` → 渲染为代码块 - 粗体、列表、标题等 → 正确渲染
6. 验证完整性
CODEBLOCK11
7. 发送成功通知
成功消息格式:
CODEBLOCK12
失败消息格式:
CODEBLOCK13
使用 message 工具发送:
CODEBLOCK14
配置
飞书归档位置
配置文件: INLINECODE51
CODEBLOCK15
使用方法:
- 1. 修改默认位置:
CODEBLOCK16
- 2. 临时指定位置(调用时传参):
CODEBLOCK17
- 3. 添加新位置:
CODEBLOCK18
获取 node_token:
CODEBLOCK19
当前配置的位置:
- 路径:学习资料抓取 → 待阅读
- 用途:新归档的文章默认放这里
- 路径:学习资料抓取
- 用途:直接归档到根目录
Twitter Cookie
Twitter Article 需要登录 Cookie:
CODEBLOCK20
文档格式
CODEBLOCK21
支持的平台
Twitter / X
URL 格式:
- - INLINECODE54
- INLINECODE55
特点:
- - 支持长文章(Twitter Article)
- 支持多图片(20+ 张)
- 需要 Cookie 认证
微信公众号
URL 格式:
特点:
普通网页
支持的网站:
- - 技术博客(Medium、Dev.to、掘金等)
- 新闻网站
- 个人博客
特点:
- - 使用
web_fetch 抓取 - 自动提取正文
- 保留基本格式
使用示例
示例 1:Twitter 链接(自动触发)
用户:https://x.com/mkdir700/status/2020652753190887566
系统行为:
- 1. 检测到 Twitter 链接,自动触发归档
- 检查去重:在知识库中搜索同名文档
- 如果不存在,继续归档流程
- 提示:"正在归档..."
- 抓取完整内容(672 行 + 24 张图片)
- 归档到飞书(学习资料抓取 → 待阅读)
- 发送成功通知:
CODEBLOCK22
示例 2:重复文档(自动跳过)
用户:https://x.com/mkdir700/status/2020652753190887566
系统行为:
- 1. 检测到 Twitter 链接,自动触发归档
- 检查去重:发现同名文档已存在
- 跳过归档,返回已存在文档的链接
- 发送通知:
CODEBLOCK23
示例 2:微信公众号(自动触发)
用户:https://mp.weixin.qq.com/s/abc123
系统行为:
- 1. 检测到微信公众号链接,自动触发归档
- 抓取文章内容
- 归档到飞书
- 发送成功通知
示例 3:手动触发
用户:保存 https://example.com/article
系统行为:
- 1. 检测到"保存"关键词 + 链接
- 自动触发归档
- 归档成功后发送通知
示例 4:归档失败
用户:https://x.com/someuser/status/123(Cookie 过期)
系统行为:
- 1. 尝试抓取,发现 Cookie 过期
- 发送失败通知:
CODEBLOCK24
脚本工具
html-to-markdown-fixed.js(推荐使用)
从 Twitter Article HTML 转换为格式化 Markdown,修复了 UTF-8 乱码和标题格式问题:
CODEBLOCK25
功能:
- - ✅ 修复 UTF-8 编码问题(使用 Buffer 确保中文字符不被截断)
- ✅ 修复标题格式问题(
# \n\n内容 → # 内容) - ✅ 自动移除替换字符
\ufffd 和 INLINECODE61 - ✅ 保留粗体(
**text**) - ✅ 保留代码块(
`shell ... ` ) - ✅ 保留列表(
- item) - ✅ 提取图片 URL
与旧版本的区别:
- -
html-to-markdown-final.js:旧版本,存在乱码和格式问题 - INLINECODE68 :新版本,已修复所有已知问题
archive-long-article.sh
处理长文章(600+ 行)+ 多图片(20+ 张):
CODEBLOCK26
功能:
- 1. 抓取完整内容(使用 html-to-markdown-fixed.js)
- 提取图片 URL
- 按图片位置分段
- 生成段落清单(manifest.json)
- 输出 feishu_doc 命令(需要在 OpenClaw 环境执行)
常见问题
Q1: 中文出现乱码(���)?
原因:UTF-8 多字节字符在传输或序列化时被截断。
解决:
- - 使用
html-to-markdown-fixed.js 而不是 INLINECODE70 - 该脚本使用 Buffer 确保 UTF-8 编码正确
- 自动移除替换字符
Q2: 标题为空,内容在下一行?
原因:Markdown 格式 # \n\n内容 导致飞书渲染时标题为空。
解决:
- - 使用 INLINECODE72
- 脚本会自动修复标题格式为 INLINECODE73
Q3: 图片位置不对,都堆在文档末尾?
原因:先写完所有文字,再统一上传图片。
解决:按图片位置分段,交替写入文本和图片。
Q4: 内容不完整,只有开头部分?
原因:长文章一次性写入失败。
解决:使用 archive-long-article.sh 脚本,分段处理。
Q5: 格式丢失(粗体、代码块)?
原因:使用了简单的文本提取,没有保留 HTML 格式。
解决:使用 html-to-markdown.js 脚本,正确转换格式。
Q4: 执行慢、消耗大量 token?
原因:手动逐个调用 feishu_doc 工具。
解决:用 bash 脚本批量处理,脚本执行不消耗 token。
Q5: Cookie 过期怎么办?
症状:抓取 Twitter Article 失败,返回登录页面。
解决:
- 1. 浏览器登录 x.com
- 打开开发者工具 → Application → Cookies
- 复制
auth_token, ct0, INLINECODE79 - 更新 INLINECODE80
成功案例
案例 1:mkdir700 长文章
- - URL:https://x.com/mkdir700/status/2020652753190887566
- 标题:月成本不到 100 元,如何实现 Token 自由
- 内容:672 行
- 图片:24 张
- 结果:✅ 完整归档,图片位置正确,格式完整
- 文档:https://qingzhao.feishu.cn/docx/NZHpd5xHxoTjYPxlVfpcaKtOnvh
案例 2:暂星的文章
- - URL:https://x.com/lumoswhy/status/2030807300257300613
- 内容:451 行
- 图片:5 张
- 结果:✅ 完整归档,格式正确
注意事项
- 1. 版权:仅用于个人学习和归档
- 去重:同一 URL 不重复归档
- 错误处理:抓取失败时提示用户,记录日志
- API 限流:每次操作间隔 0.3 秒
- Cookie 管理:定期检查 Twitter Cookie 是否过期
改进历史
- - 2026-03-10:初始版本,支持基本归档
- 2026-03-11:重大改进
- 支持长文章(600+ 行)
- 支持多图片(20+ 张)
- 图片位置正确(分段写入 + 交替插入)
- 格式完整保留(粗体、代码块、列表)
- 添加
archive-long-article.sh 脚本
- 添加完整性验证
- 添加飞书归档位置配置(支持灵活修改目标位置)
-
添加自动触发机制(检测 Twitter/微信公众号链接)
-
添加成功/失败通知(自动推送消息给用户)
-
支持微信公众号(mp.weixin.qq.com)
最后更新:2026-03-11 20:55
维护者:影
最新改进(2026-03-11)
使用 turndown 库
article-archiver 现在使用业界标准的 turndown 库进行 HTML 到 Markdown 的转换,确保高质量的格式保留。
核心脚本: INLINECODE82
关键特性:
- 1. ✅ 代码块支持:正确识别
<pre>/<code> 标签,保留语言标注(bash、json、yaml、text 等) - ✅ 图片支持:保留 Markdown 图片语法
,飞书会自动下载并转换 - ✅ 格式保留:粗体、列表、标题、引用块、链接等全部正确转换
- ✅ GFM 扩展:支持 GitHub Flavored Markdown(表格、删除线等)
技术栈:
- - Playwright:无头浏览器,处理动态加载的内容
- turndown:HTML 转 Markdown(8.8k GitHub stars)
- turndown-plugin-gfm:GFM 扩展支持
代码块处理:
// 自定义代码块规则:保留语言标注
turndownService.addRule('codeBlock', {
filter: function (node) {
return node.nodeName === 'PRE' && node.querySelector('code');
},
replacement: function (content, node) {
const code = node.querySelector('code');
const language = code.className.replace('language-', '') || '';
const text = code.textContent;
return '\n\n
' + language + '\n' + text + '\n``
\n\n';
}
});
CODEBLOCK28 markdown
> **原始链接:** https://x.com/username/status/123456789
> **归档时间:** 2026-03-11 22:50:00
> **来源:** x.com (Twitter Article)
> **作者:** 作者名 (@username)
---
CODEBLOCK29 bash
cd ~/.openclaw/workspace/skills/article-archiver
npm install turndown turndown-plugin-gfm playwright
CODEBLOCK30 bash
# 提取文章(包含代码块和图片)
node scripts/html-to-markdown-final.js \
"https://x.com/username/status/123456789" \
"$(cat config/twitter-cookies.txt)" \
> /tmp/article.json
# 检查提取结果
jq '.content' /tmp/article.json | head -50
# 统计代码块和图片
echo "代码块: $(jq -r '.content' /tmp/article.json | grep -c '`
')"
echo "图片: $(jq -r '.content' /tmp/article.json | grep -c '!\[')"
CODEBLOCK31 javascript
// 检查是否为飞书嵌入文档
const firstParagraph = document.querySelector('article p')?.textContent;
if (firstParagraph?.includes('暂时无法在飞书文档外展示此内容')) {
// 这是飞书嵌入文档,无法抓取
}
CODEBLOCK32 bash
feishu_wiki create \
--space-id 7527734827164909572 \
--parent-node NqZvwBqMTiTEtkkMsRoc76rznce \
--title "$TITLE" \
--obj-type docx
CODEBLOCK33 bash
# 检查去重
RESULT=$(bash scripts/check-duplicate.sh "$URL")
# 解析结果
if [[ $RESULT == EXISTS:* ]]; then
NODE_TOKEN=$(echo "$RESULT" | cut -d: -f2)
TITLE=$(echo "$RESULT" | cut -d: -f3)
echo "⚠️ 文档已存在:https://qingzhao.feishu.cn/wiki/$NODE_TOKEN"
exit 0
fi
CODEBLOCK34 bash
bash scripts/check-duplicate.sh "$URL"
CODEBLOCK35 bash
node scripts/html-to-markdown-final.js "$URL" "$(cat config/twitter-cookies.txt)" > /tmp/article.json
CODEBLOCK36 bash
TITLE=$(jq -r '.title' /tmp/article.json)
feishu_wiki create --space-id 7527734827164909572 --parent-node NqZvwBqMTiTEtkkMsRoc76rznce --title "$TITLE" --obj-type docx
CODEBLOCK37 javascript
// 提取元数据
const metadata = JSON.parse(await exec({ command: 'cat /tmp/article.json' }));
const timestamp = new Date().toISOString().slice(0, 19).replace('T', ' ');
// 判断来源
let source, authorText;
if (url.includes('x.com') || url.includes('twitter.com')) {
source = 'x.com (Twitter Article)';
authorText = ${metadata.author} (@${metadata.username})
;
} else if (url.includes('mp.weixin.qq.com')) {
source = '微信公众号';
authorText = metadata.author;
} else {
source = url.match(/https?:\/\/([^/]+)/)[1];
authorText = metadata.author;
}
// 写入元数据头部
await feishu_doc({
action: 'append',
doc_token: DOC_TOKEN,
content: >
原始链接: ${url}
归档时间: ${timestamp}
来源: ${source}
作者: ${authorText}
---
});
CODEBLOCK38 javascript
// 读取正文内容
const content = await read({ path: '/tmp/article.json' });
const articleContent = JSON.parse(content).content;
// 写入正文
await feishu_doc({
action: 'append',
doc_token: DOC_TOKEN,
content: articleContent
});
CODEBLOCK39 bash
feishu_doc list_blocks --doc_token $DOC_TOKEN | jq '.blocks | length'
CODEBLOCK40 javascript
const content = await read({ path: '/tmp/file.md' });
await feishu_doc({ action: 'append', content: content });
`
**问题 7:UTF-8 乱码(字符被截断)**
- **症状**:那么��读和自媒体("阅"字显示为 ��)
- **原因**:多字节 UTF-8 字符在传输或分段时被截断
- **解决**:
- 确保完整的字符串传递,不要在字符中间截断
- 使用 OpenClaw 的 read 工具读取文件(自动处理编码)
- 避免使用 bash 命令的字符串截取(如 head -c)
**问题 8:归档位置错误**
- **症状**:文档创建在错误的知识库位置
- **原因**:使用了错误的 parentnodetoken
- **解决**:
- 检查配置文件:config/feishu-locations.sh
- 确认 DEFAULTPARENTNODE 的值
- 正确位置:NqZvwBqMTiTEtkkMsRoc76rznce(学习资料抓取 → 待阅读)
- 错误位置:YziUwLVlBi9BX7kVtkJcf7nQns2`(旧位置)
Article Archiver
自动抓取网页文章和 Twitter Article,完整归档到飞书文档。
⚠️ 关键实现要点(必读)
1. 必须读取配置文件获取归档位置:
bash
source ~/.openclaw/workspace/skills/article-archiver/config/feishu-locations.sh
使用 $DEFAULTSPACEID 和 $DEFAULTPARENTNODE
2. 图片处理流程:
- - 使用 agent-browser 打开页面
- 滚动加载所有图片(懒加载)
- 用 img.preview 或类似选择器过滤内容图片(排除头像、图标)
- 分析每张图片在文章中的位置(找到前面最近的段落文本)
- 下载图片到 /tmp/
- 使用 feishudoc uploadimage 并指定 afterblockid 插入到正确位置
- 关键:uploadimage 支持 afterblock_id 参数,可以精确控制图片位置
3. 样式保留:
- - 引用块:使用 > 开头
- 粗体:使用 text
- 列表:使用 - 或 1.
- 代码块:使用
4. 内容传递方式:
- - 使用 OpenClaw 的 read 工具读取文件
- 不能在 feishu_doc 参数中使用 $(cat file)
- 大文件分批处理(每批 100 行)
5. 去重检查:
- - 归档前先搜索同名文档
- 如果存在,返回已有文档链接
- 不要重复创建
6. 防止乱码和格式问题:
- 使用 html-to-markdown-fixed.js(而不是 html-to-markdown-final.js)
- 该脚本使用 Buffer 确保 UTF-8 编码正确,避免中文字符被截断
- 自动移除替换字符 \ufffd 和 ���
- 脚本会自动修复 # \n\n内容 格式为 # 内容
- 避免飞书渲染时标题为空、内容在下一行的问题
- 写入飞书前,检查 JSON 输出中是否有 \ufffd 或 ���
- 检查标题格式是否正确(标题标记和内容在同一行)
- 不要手动重写原文内容,必须从原始来源精确提取
- 长文本分段写入时,不要在多字节字符中间截断
7. 避免重复标题:
- - 飞书文档已经有页面标题(Page title),不要在正文开头再写一次标题
- 从原文提取内容时,跳过第一个 H1 标题(通常就是文章标题)
- 如果 web_fetch 或 agent-browser 提取的内容以标题开头,删除第一行
- 正文应该从引言、摘要或第一段正文开始
- 元数据后直接接正文,不要插入标题
8. 元数据格式(重要):
- - 必须使用引用块格式(灰色背景,有样式,好看)
- 标准格式(参考 https://qingzhao.feishu.cn/wiki/ZVCFwN7bci1uyhknLpucA18FnSe):
markdown
>
原始链接: https://example.com/article
>
归档时间: 2026-03-12 22:16:00
>
来源: example.com
>
作者: 张三 (@username)
- 每行以 > 开头(引用块)
- 字段名用
粗体 + 冒号
- 字段值用普通文本
- 元数据和正文之间用分隔线 --- 分开
- Markdown 表格(会被渲染成飞书表格,不美观)
- 普通文本(没有样式,不好看)
- Emoji(📄、📅、✍️)在引用块中显示不好
9. 图片位置精确控制:
- - 图片必须插入到正确的段落之后,不能随意放置
- 使用 feishudoc uploadimage 时,必须指定 afterblockid
- 步骤:
1. 先写入全部正文内容
2. 使用 feishu
doc listblocks 获取所有 block_id
3. 根据图片前面的文本内容,找到对应的 block_id
4. 用 after
blockid 参数将图片插入到正确位置
- - 不要在写入正文时就尝试插入图片(会导致位置错乱)
核心功能
- - ✅ 支持长文章(600+ 行)
- ✅ 支持多图片(20+ 张),图片插入到正确位置
- ✅ 保留完整格式(粗体、代码块、列表)
- ✅ 按月份自动组织
- ✅ 自动去重(检测重复 URL)
使用方法
触发条件
自动触发(无需用户明确说归档):
- - 用户发送 Twitter/X 链接:https://x.com/... 或 https://twitter.com/...
- 用户发送微信公众号链接:https://mp.weixin.qq.com/...
- 用户说保存 [链接]、记录 [链接]、收藏 [链接]
手动触发:
- - 用户明确说归档这篇文章、保存到飞书
- 用户发送其他网页链接并询问是否归档
触发后的行为:
- 1. 立即开始归档,不要询问确认
- 归档过程中可以简短提示正在归档...
- 归档成功后发送通知消息(包含文档链接)
- 归档失败时说明原因,提供解决建议
归档流程
0. 去重检查(必须先执行)
在开始归档前,必须先检查文档是否已存在:
bash
1. 提取文章标题(使用修复版脚本)
TITLE=$(node scripts/html-to-markdown-fixed.js $URL $(cat config/twitter-cookies.txt) | jq -r .title)
2. 在知识库中搜索同名文档
feishu_wiki nodes --space-id 7527734827164909572 --parent-node NqZvwBqMTiTEtkkMsRoc76rznce | \
jq -r .nodes[] | select(.title == $TITLE) | .node_token
3. 如果找到同名文档,跳过归档
if [ -n $EXISTING_NODE ]; then
echo ⚠️ 文档已存在,跳过归档
echo 已存在的文档:https://qingzhao.feishu.cn/wiki/$EXISTING_NODE
exit 0
fi
去重规则:
- - 标题完全一样 → 视为已存在
- 跳过归档,返回已存在文档的链接
- 不要重复创建相同标题的文档
实现方式:
- 1. 先提取文章标题(不需要完整内容)
- 在目标位置搜索同名文档
- 如果存在,直接返回链接并跳过
- 如果不存在,继续正常归档流程
1. 识别文章类型
javascript
if (url.includes(x.com) && url.includes(/status/)) {
// Twitter Article - 需要特殊处理
return twitter-article;
} else {
// 普通网页
return web-page;
}
2. 抓取内容
Twitter Article(长文章 + 多图片):
bash
使用修复版脚本(解决乱码和格式问题)
cd ~/.openclaw/workspace/skills/article-archiver
node scripts/html-to-markdown-fixed.js $URL $(cat config/twitter-cookies.txt) > /tmp/article.json
普通网页:
bash
使用 web_fetch
web_fetch --url
--extract-mode markdown
3. 分段写入(核心方法)
⚠️ 重要:正确的文件内容传递方式
在 OpenClaw 会话中,不能使用 bash 命令替换 $(cat file) 直接传给 feishu_doc,因为会被当成字面字符串。
正确方法:
方法 1:使用 OpenClaw 的 read 工具(推荐)
javascript
// 1. 先用 read 工具读取文件
const content = await read({ path: /tmp/content.txt });
// 2. 然后传给 feishu_doc
await feishu_doc({
action: append,
doctoken: DOCTOKEN,
content: content
});
方法 2:分批读取和上传(适合大文件)
javascript
// 读取文件总行数
const totalLines = parseInt(await exec({
command