Initial commit with translated description
This commit is contained in:
192
lib/docx.js
Normal file
192
lib/docx.js
Normal file
@@ -0,0 +1,192 @@
|
||||
|
||||
async function fetchDocxContent(documentId, accessToken) {
|
||||
// 1. Get document info for title
|
||||
const infoUrl = `https://open.feishu.cn/open-apis/docx/v1/documents/${documentId}`;
|
||||
const infoRes = await fetch(infoUrl, {
|
||||
headers: { 'Authorization': `Bearer ${accessToken}` }
|
||||
});
|
||||
const infoData = await infoRes.json();
|
||||
let title = "Untitled Docx";
|
||||
if (infoData.code === 0 && infoData.data && infoData.data.document) {
|
||||
title = infoData.data.document.title;
|
||||
}
|
||||
|
||||
// 2. Fetch all blocks
|
||||
// List blocks API: GET https://open.feishu.cn/open-apis/docx/v1/documents/{document_id}/blocks
|
||||
// Use pagination if necessary, fetching all for now (basic implementation)
|
||||
let blocks = [];
|
||||
let pageToken = '';
|
||||
let hasMore = true;
|
||||
|
||||
while (hasMore) {
|
||||
const url = `https://open.feishu.cn/open-apis/docx/v1/documents/${documentId}/blocks?page_size=500${pageToken ? `&page_token=${pageToken}` : ''}`;
|
||||
const response = await fetch(url, {
|
||||
headers: { 'Authorization': `Bearer ${accessToken}` }
|
||||
});
|
||||
const data = await response.json();
|
||||
|
||||
if (data.code !== 0) {
|
||||
throw new Error(`Failed to fetch docx blocks: ${data.msg}`);
|
||||
}
|
||||
|
||||
if (data.data && data.data.items) {
|
||||
blocks = blocks.concat(data.data.items);
|
||||
}
|
||||
|
||||
hasMore = data.data.has_more;
|
||||
pageToken = data.data.page_token;
|
||||
}
|
||||
|
||||
const markdown = convertBlocksToMarkdown(blocks);
|
||||
return { title, content: markdown };
|
||||
}
|
||||
|
||||
function convertBlocksToMarkdown(blocks) {
|
||||
if (!blocks || blocks.length === 0) return "";
|
||||
|
||||
let md = [];
|
||||
|
||||
for (const block of blocks) {
|
||||
const type = block.block_type;
|
||||
|
||||
switch (type) {
|
||||
case 1: // page
|
||||
break;
|
||||
case 2: // text (paragraph)
|
||||
md.push(parseText(block.text));
|
||||
break;
|
||||
case 3: // heading1
|
||||
md.push(`# ${parseText(block.heading1)}`);
|
||||
break;
|
||||
case 4: // heading2
|
||||
md.push(`## ${parseText(block.heading2)}`);
|
||||
break;
|
||||
case 5: // heading3
|
||||
md.push(`### ${parseText(block.heading3)}`);
|
||||
break;
|
||||
case 6: // heading4
|
||||
md.push(`#### ${parseText(block.heading4)}`);
|
||||
break;
|
||||
case 7: // heading5
|
||||
md.push(`##### ${parseText(block.heading5)}`);
|
||||
break;
|
||||
case 8: // heading6
|
||||
md.push(`###### ${parseText(block.heading6)}`);
|
||||
break;
|
||||
case 9: // heading7
|
||||
md.push(`####### ${parseText(block.heading7)}`);
|
||||
break;
|
||||
case 10: // heading8
|
||||
md.push(`######## ${parseText(block.heading8)}`);
|
||||
break;
|
||||
case 11: // heading9
|
||||
md.push(`######### ${parseText(block.heading9)}`);
|
||||
break;
|
||||
case 12: // bullet
|
||||
md.push(`- ${parseText(block.bullet)}`);
|
||||
break;
|
||||
case 13: // ordered
|
||||
md.push(`1. ${parseText(block.ordered)}`);
|
||||
break;
|
||||
case 14: // code
|
||||
md.push('```' + (block.code?.style?.language === 1 ? '' : '') + '\n' + parseText(block.code) + '\n```');
|
||||
break;
|
||||
case 15: // quote
|
||||
md.push(`> ${parseText(block.quote)}`);
|
||||
break;
|
||||
case 27: // image
|
||||
md.push(``);
|
||||
break;
|
||||
default:
|
||||
// Ignore unknown blocks for now
|
||||
console.error(`Skipped block type: ${type}`, JSON.stringify(block).substring(0, 200));
|
||||
md.push(`[UNSUPPORTED BLOCK TYPE: ${type}]`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return md.join('\n\n');
|
||||
}
|
||||
|
||||
async function appendDocxContent(documentId, content, accessToken) {
|
||||
// 1. Convert markdown content to Feishu blocks
|
||||
const blocks = convertMarkdownToBlocks(content);
|
||||
|
||||
// 2. Append to the end of the document (root block)
|
||||
// POST https://open.feishu.cn/open-apis/docx/v1/documents/{document_id}/blocks/{block_id}/children
|
||||
// Use documentId as block_id to append to root
|
||||
const url = `https://open.feishu.cn/open-apis/docx/v1/documents/${documentId}/blocks/${documentId}/children`;
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${accessToken}`,
|
||||
'Content-Type': 'application/json; charset=utf-8'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
children: blocks,
|
||||
index: -1 // Append to end
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
if (data.code !== 0) {
|
||||
throw new Error(`Failed to append to docx: ${data.msg}`);
|
||||
}
|
||||
|
||||
return { success: true, appended_blocks: data.data.children };
|
||||
}
|
||||
|
||||
function convertMarkdownToBlocks(markdown) {
|
||||
// Simple parser: split by newlines, treat # as headers, others as text
|
||||
// For robustness, this should be a real parser. Here we implement a basic one.
|
||||
const lines = markdown.split('\n');
|
||||
const blocks = [];
|
||||
|
||||
for (const line of lines) {
|
||||
const trimmed = line.trim();
|
||||
if (!trimmed) continue;
|
||||
|
||||
if (trimmed.startsWith('# ')) {
|
||||
blocks.push({ block_type: 3, heading1: { elements: [{ text_run: { content: trimmed.substring(2) } }] } });
|
||||
} else if (trimmed.startsWith('## ')) {
|
||||
blocks.push({ block_type: 4, heading2: { elements: [{ text_run: { content: trimmed.substring(3) } }] } });
|
||||
} else if (trimmed.startsWith('### ')) {
|
||||
blocks.push({ block_type: 5, heading3: { elements: [{ text_run: { content: trimmed.substring(4) } }] } });
|
||||
} else if (trimmed.startsWith('- ')) {
|
||||
blocks.push({ block_type: 12, bullet: { elements: [{ text_run: { content: trimmed.substring(2) } }] } });
|
||||
} else {
|
||||
// Default to text (paragraph)
|
||||
blocks.push({ block_type: 2, text: { elements: [{ text_run: { content: line } }] } });
|
||||
}
|
||||
}
|
||||
return blocks;
|
||||
}
|
||||
|
||||
function parseText(blockData) {
|
||||
if (!blockData || !blockData.elements) return "";
|
||||
|
||||
return blockData.elements.map(el => {
|
||||
if (el.text_run) {
|
||||
let text = el.text_run.content;
|
||||
const style = el.text_run.text_element_style;
|
||||
if (style) {
|
||||
if (style.bold) text = `**${text}**`;
|
||||
if (style.italic) text = `*${text}*`;
|
||||
if (style.strikethrough) text = `~~${text}~~`;
|
||||
if (style.inline_code) text = `\`${text}\``;
|
||||
if (style.link) text = `[${text}](${style.link.url})`;
|
||||
}
|
||||
return text;
|
||||
}
|
||||
if (el.mention_doc) {
|
||||
return `[Doc: ${el.mention_doc.token}]`;
|
||||
}
|
||||
return "";
|
||||
}).join("");
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
fetchDocxContent,
|
||||
appendDocxContent
|
||||
};
|
||||
Reference in New Issue
Block a user