UNPKG

feishu-mcp

Version:

Model Context Protocol server for Feishu integration

111 lines (110 loc) 4.71 kB
import { Logger } from '../../../utils/logger.js'; import { ParamUtils } from '../../../utils/paramUtils.js'; import { FeishuBaseApiService } from '../../../services/feishu/FeishuBaseApiService.js'; /** * 飞书文档服务 * 负责文档的创建与信息查询 */ export class FeishuDocumentService extends FeishuBaseApiService { constructor(authService) { super(authService); } /** * 创建飞书文档 * @param title 文档标题 * @param folderToken 目标文件夹的 Token,文档将创建在此文件夹下 * @returns 创建的文档信息,包含 document_id、title 等字段 */ async createDocument(title, folderToken) { try { const response = await this.post('/docx/v1/documents', { title, folder_token: folderToken }); return response; } catch (error) { this.handleApiError(error, '创建飞书文档失败'); } } /** * 获取文档信息,支持普通文档和 Wiki 文档 * 当 documentType 未指定时,自动根据 documentId 是否包含 /wiki/ 路径判断类型 * @param documentId 文档 ID、文档 URL 或 Wiki 节点 Token/URL * @param documentType 文档类型,'document' 为普通文档,'wiki' 为知识库文档;不传则自动检测 * @returns 文档信息对象,额外附加 _type 字段标识来源('document' | 'wiki'), * Wiki 文档额外附加 documentId 字段作为 obj_token 的别名 */ async getDocumentInfo(documentId, documentType) { try { let isWikiLink; if (documentType === 'wiki') { isWikiLink = true; } else if (documentType === 'document') { isWikiLink = false; } else { isWikiLink = documentId.includes('/wiki/'); } if (isWikiLink) { const wikiToken = ParamUtils.processWikiToken(documentId); const response = await this.get('/wiki/v2/spaces/get_node', { token: wikiToken, obj_type: 'wiki' }); if (!response.node || !response.node.obj_token) { throw new Error(`无法从Wiki节点获取文档ID: ${wikiToken}`); } const node = response.node; Logger.debug(`获取Wiki文档信息: ${wikiToken} -> documentId: ${node.obj_token}`); return { ...node, documentId: node.obj_token, _type: 'wiki' }; } else { const normalizedDocId = ParamUtils.processDocumentId(documentId); const response = await this.get(`/docx/v1/documents/${normalizedDocId}`); Logger.debug(`获取普通文档信息: ${normalizedDocId}`); return { ...response, _type: 'document' }; } } catch (error) { this.handleApiError(error, '获取文档信息失败'); } } /** * 获取文档的纯文本内容 * @param documentId 文档 ID 或 URL * @param lang 语言,0 为中文,1 为英文,默认 0 * @returns 文档的纯文本内容字符串 */ async getDocumentContent(documentId, lang = 0) { try { const normalizedDocId = ParamUtils.processDocumentId(documentId); const response = await this.get(`/docx/v1/documents/${normalizedDocId}/raw_content`, { lang }); return response.content; } catch (error) { this.handleApiError(error, '获取文档内容失败'); } } /** * 获取文档的所有块结构,自动处理分页直到获取全部数据 * @param documentId 文档 ID 或 URL * @param pageSize 每次分页请求的块数量,默认 500 * @returns 文档的所有块对象数组 */ async getDocumentBlocks(documentId, pageSize = 500) { try { const normalizedDocId = ParamUtils.processDocumentId(documentId); const endpoint = `/docx/v1/documents/${normalizedDocId}/blocks`; let pageToken = ''; let allBlocks = []; do { const params = { page_size: pageSize, document_revision_id: -1 }; if (pageToken) params.page_token = pageToken; const response = await this.get(endpoint, params); allBlocks = [...allBlocks, ...(response.items || [])]; pageToken = response.page_token; } while (pageToken); return allBlocks; } catch (error) { this.handleApiError(error, '获取文档块结构失败'); } } }