Second commit
This commit is contained in:
Executable
+116
@@ -0,0 +1,116 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* 飞书文档复制工具
|
||||
*
|
||||
* 功能:
|
||||
* 1. 读取源飞书文档(wiki/docx)的完整内容
|
||||
* 2. 在你的飞书账号下创建新文档
|
||||
* 3. 复制所有文本、格式、图片到新文档
|
||||
*
|
||||
* 用法:node feishu-copy.mjs <source_wiki_url> [目标文件夹token]
|
||||
*
|
||||
* 示例:
|
||||
* node feishu-copy.mjs https://xxx.feishu.cn/wiki/ABC123
|
||||
* node feishu-copy.mjs https://xxx.feishu.cn/wiki/ABC123 fldcnXXXXXXX
|
||||
*/
|
||||
import { execSync } from 'child_process';
|
||||
import { unlinkSync } from 'fs';
|
||||
import { basename } from 'path';
|
||||
|
||||
const argv = process.argv.slice(2);
|
||||
if (!argv[0] || argv[0] === '--help' || argv[0] === '-h') {
|
||||
console.log(`
|
||||
用法:node feishu-copy.mjs <source_url> [目标文件夹token]
|
||||
|
||||
参数:
|
||||
source_url 源文档 URL(wiki 或 docx)
|
||||
目标文件夹token 可选,新文档放在指定文件夹下
|
||||
|
||||
示例:
|
||||
node feishu-copy.mjs https://yitanger.feishu.cn/wiki/R9NywV2i9izjuqkPd63cWuafn4g
|
||||
node feishu-copy.mjs https://yitanger.feishu.cn/wiki/ABC123 fldcnXXXXXXX
|
||||
`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const sourceUrl = argv[0].trim();
|
||||
const targetFolder = argv[1]?.trim();
|
||||
|
||||
console.log('╔════════════════════════════════════════════╗');
|
||||
console.log('║ 飞书文档复制工具 ║');
|
||||
console.log('╚════════════════════════════════════════════╝');
|
||||
console.log(`📎 源文档: ${sourceUrl}\n`);
|
||||
|
||||
// ── 1. 解析源文档 token ───────────────────────────────────────────────────────
|
||||
console.log('🔍 正在解析源文档...');
|
||||
let obj_token, title;
|
||||
|
||||
if (sourceUrl.includes('/wiki/')) {
|
||||
// wiki → 先解析成 docx token
|
||||
const nodeInfo = larkJson(`lark-cli wiki +node-get --node-token "${sourceUrl}"`);
|
||||
if (!nodeInfo.ok) die('获取 wiki 节点失败', nodeInfo);
|
||||
obj_token = nodeInfo.data.obj_token;
|
||||
title = nodeInfo.data.title;
|
||||
} else if (sourceUrl.includes('/docx/') || sourceUrl.match(/[A-Za-z0-9]{20,}/)) {
|
||||
// docx URL 或直接是 token
|
||||
obj_token = sourceUrl.match(/[A-Za-z0-9]{20,}/)?.[0] || sourceUrl;
|
||||
// 先 fetch 一次拿标题
|
||||
const docInfo = larkJson(`lark-cli docs +fetch --doc ${obj_token} --api-version v2`);
|
||||
if (!docInfo.ok) die('获取文档失败', docInfo);
|
||||
title = docInfo.data.document.title || '未命名文档';
|
||||
} else {
|
||||
die('不支持的 URL 格式', { url: sourceUrl });
|
||||
}
|
||||
|
||||
console.log(`✅ 标题:${title}`);
|
||||
console.log(`✅ obj_token:${obj_token}\n`);
|
||||
|
||||
// ── 2. 导出源文档为 Markdown ─────────────────────────────────────────────────
|
||||
console.log('📄 正在导出源文档为 Markdown...');
|
||||
const exportResult = larkJson(
|
||||
`lark-cli drive +export --token ${obj_token} --doc-type docx --file-extension markdown --output-dir . --overwrite`
|
||||
);
|
||||
if (!exportResult.ok) die('导出失败', exportResult);
|
||||
|
||||
const mdPath = exportResult.data.saved_path;
|
||||
const mdFile = basename(mdPath); // 相对文件名,lark-cli 只接受相对路径
|
||||
console.log(`✅ 已导出(${mdFile})\n`);
|
||||
|
||||
// ── 3. 导入为新文档 ──────────────────────────────────────────────────────────
|
||||
console.log('📝 正在导入为新文档...');
|
||||
const importCmd = targetFolder
|
||||
? `lark-cli drive +import --file "@${mdFile}" --type markdown --folder-token ${targetFolder}`
|
||||
: `lark-cli drive +import --file "@${mdFile}" --type markdown`;
|
||||
|
||||
const cwd = process.cwd();
|
||||
const importResult = larkJson(importCmd, { cwd });
|
||||
if (!importResult.ok) die('导入失败', importResult);
|
||||
|
||||
const newDocToken = importResult.data?.token || importResult.data?.document_id;
|
||||
if (!newDocToken) die('导入返回异常', importResult);
|
||||
|
||||
// 删除临时导出文件
|
||||
try { unlinkSync(mdPath); } catch {}
|
||||
|
||||
console.log(`✅ 新文档已创建并写入内容`);
|
||||
console.log(`✅ 新文档 token:${newDocToken}\n`);
|
||||
|
||||
console.log('╔════════════════════════════════════════════╗');
|
||||
console.log(`║ ✅ 复制完成!`);
|
||||
console.log(`║ 📄 新文档: https://feishu.cn/docx/${newDocToken}`);
|
||||
console.log(`║ 💡 图片为飞书链接,需登录后可见`);
|
||||
console.log('╚════════════════════════════════════════════╝');
|
||||
|
||||
// ── helpers ──────────────────────────────────────────────────────────────────
|
||||
function larkJson(cmd, opts = {}) {
|
||||
try {
|
||||
return JSON.parse(execSync(cmd, { encoding: 'utf-8', ...opts }));
|
||||
} catch (e) {
|
||||
try { return JSON.parse(e.stderr || e.stdout || '{}'); } catch { return {}; }
|
||||
}
|
||||
}
|
||||
|
||||
function die(msg, info) {
|
||||
console.error(`❌ ${msg}:`, info?.error?.message || JSON.stringify(info));
|
||||
process.exit(1);
|
||||
}
|
||||
Reference in New Issue
Block a user