feishu-mcp 0.0.11 → 0.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.en.md +201 -0
- package/README.md +164 -164
- package/dist/cli.js +0 -0
- package/dist/config.js +100 -22
- package/dist/mcp/tools/feishuTools.js +30 -1
- package/dist/services/feishu.js +3 -0
- package/dist/services/feishuApiService.js +46 -0
- package/dist/types/feishuSchema.js +2 -0
- package/dist/utils/logger.js +2 -2
- package/package.json +74 -74
- package/dist/services/feishuBlockService.js +0 -179
- package/dist/services/feishuService.js +0 -475
|
@@ -1,475 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @deprecated 这个文件已被弃用,所有功能已迁移到FeishuApiService类。
|
|
3
|
-
* 请使用FeishuApiService访问飞书API。
|
|
4
|
-
* 此文件仅保留作为参考和兼容性目的。
|
|
5
|
-
*/
|
|
6
|
-
import axios, { AxiosError } from "axios";
|
|
7
|
-
import { Logger } from "../utils/logger.js";
|
|
8
|
-
import { formatErrorMessage } from "../utils/error.js";
|
|
9
|
-
import { normalizeDocumentId, normalizeWikiToken } from "../utils/document.js";
|
|
10
|
-
import { createCodeBlockContent, createTextBlockContent, createHeadingBlockContent, createListBlockContent } from "./feishuBlockService.js";
|
|
11
|
-
export class FeishuService {
|
|
12
|
-
constructor(appId, appSecret) {
|
|
13
|
-
Object.defineProperty(this, "appId", {
|
|
14
|
-
enumerable: true,
|
|
15
|
-
configurable: true,
|
|
16
|
-
writable: true,
|
|
17
|
-
value: void 0
|
|
18
|
-
});
|
|
19
|
-
Object.defineProperty(this, "appSecret", {
|
|
20
|
-
enumerable: true,
|
|
21
|
-
configurable: true,
|
|
22
|
-
writable: true,
|
|
23
|
-
value: void 0
|
|
24
|
-
});
|
|
25
|
-
Object.defineProperty(this, "baseUrl", {
|
|
26
|
-
enumerable: true,
|
|
27
|
-
configurable: true,
|
|
28
|
-
writable: true,
|
|
29
|
-
value: "https://open.feishu.cn/open-apis"
|
|
30
|
-
});
|
|
31
|
-
Object.defineProperty(this, "accessToken", {
|
|
32
|
-
enumerable: true,
|
|
33
|
-
configurable: true,
|
|
34
|
-
writable: true,
|
|
35
|
-
value: null
|
|
36
|
-
});
|
|
37
|
-
Object.defineProperty(this, "tokenExpireTime", {
|
|
38
|
-
enumerable: true,
|
|
39
|
-
configurable: true,
|
|
40
|
-
writable: true,
|
|
41
|
-
value: null
|
|
42
|
-
});
|
|
43
|
-
Object.defineProperty(this, "MAX_TOKEN_LIFETIME", {
|
|
44
|
-
enumerable: true,
|
|
45
|
-
configurable: true,
|
|
46
|
-
writable: true,
|
|
47
|
-
value: 2 * 60 * 60 * 1000
|
|
48
|
-
}); // 2小时的毫秒数
|
|
49
|
-
this.appId = appId;
|
|
50
|
-
this.appSecret = appSecret;
|
|
51
|
-
}
|
|
52
|
-
// 包装和重新抛出错误的辅助方法
|
|
53
|
-
wrapAndThrowError(message, originalError) {
|
|
54
|
-
// 记录原始错误信息
|
|
55
|
-
Logger.error(`${message}:`, originalError);
|
|
56
|
-
// 如果原始错误已经是FeishuError格式,添加更多上下文后重新抛出
|
|
57
|
-
if (originalError && typeof originalError === 'object' && 'status' in originalError && 'err' in originalError) {
|
|
58
|
-
// 添加更具体的错误信息
|
|
59
|
-
throw originalError;
|
|
60
|
-
}
|
|
61
|
-
// 如果是AxiosError,提取有用信息并规范化错误格式
|
|
62
|
-
if (originalError instanceof AxiosError && originalError.response) {
|
|
63
|
-
// 获取响应数据
|
|
64
|
-
const responseData = originalError.response.data;
|
|
65
|
-
const error = {
|
|
66
|
-
status: originalError.response.status,
|
|
67
|
-
err: formatErrorMessage(originalError, message),
|
|
68
|
-
apiError: responseData
|
|
69
|
-
};
|
|
70
|
-
// 针对HTTP状态码添加帮助信息
|
|
71
|
-
if (originalError.response.status === 404) {
|
|
72
|
-
Logger.info("404错误: 请求的资源未找到。这可能是因为文档/块ID不正确,或者资源已被删除。");
|
|
73
|
-
}
|
|
74
|
-
else if (originalError.response.status === 403) {
|
|
75
|
-
Logger.info("403错误: 权限不足。这可能是因为应用没有足够的权限或者文档访问设置限制。");
|
|
76
|
-
}
|
|
77
|
-
else if (originalError.response.status === 401) {
|
|
78
|
-
Logger.info("401错误: 未授权。这可能是因为访问令牌已过期或无效。系统将尝试重新获取令牌。");
|
|
79
|
-
}
|
|
80
|
-
else if (originalError.response.status === 429) {
|
|
81
|
-
Logger.info("429错误: 请求频率超过限制。请减少请求频率或增加请求之间的间隔时间。");
|
|
82
|
-
}
|
|
83
|
-
throw error;
|
|
84
|
-
}
|
|
85
|
-
// 处理其他类型的错误,包装为一致的格式
|
|
86
|
-
const errorMessage = originalError instanceof Error
|
|
87
|
-
? originalError.message
|
|
88
|
-
: (typeof originalError === 'string' ? originalError : '未知错误');
|
|
89
|
-
throw {
|
|
90
|
-
status: 500,
|
|
91
|
-
err: formatErrorMessage(originalError, message),
|
|
92
|
-
apiError: {
|
|
93
|
-
code: -1,
|
|
94
|
-
msg: errorMessage,
|
|
95
|
-
error: originalError
|
|
96
|
-
}
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
isTokenExpired() {
|
|
100
|
-
if (!this.accessToken || !this.tokenExpireTime)
|
|
101
|
-
return true;
|
|
102
|
-
return Date.now() >= this.tokenExpireTime;
|
|
103
|
-
}
|
|
104
|
-
async getAccessToken() {
|
|
105
|
-
if (this.accessToken && !this.isTokenExpired()) {
|
|
106
|
-
Logger.log('使用现有访问令牌,未过期');
|
|
107
|
-
return this.accessToken;
|
|
108
|
-
}
|
|
109
|
-
try {
|
|
110
|
-
const url = `${this.baseUrl}/auth/v3/tenant_access_token/internal`;
|
|
111
|
-
const requestData = {
|
|
112
|
-
app_id: this.appId,
|
|
113
|
-
app_secret: this.appSecret,
|
|
114
|
-
};
|
|
115
|
-
Logger.log('开始获取新的访问令牌...');
|
|
116
|
-
Logger.log(`请求URL: ${url}`);
|
|
117
|
-
Logger.log(`请求方法: POST`);
|
|
118
|
-
Logger.log(`请求数据: ${JSON.stringify(requestData, null, 2)}`);
|
|
119
|
-
const response = await axios.post(url, requestData);
|
|
120
|
-
Logger.log(`响应状态码: ${response?.status}`);
|
|
121
|
-
Logger.log(`响应头: ${JSON.stringify(response.headers, null, 2)}`);
|
|
122
|
-
Logger.log(`响应数据: ${JSON.stringify(response.data, null, 2)}`);
|
|
123
|
-
if (response.data.code !== 0) {
|
|
124
|
-
Logger.error(`获取访问令牌失败,错误码: ${response.data.code}, 错误信息: ${response.data.msg}`);
|
|
125
|
-
throw {
|
|
126
|
-
status: response.status,
|
|
127
|
-
err: response.data.msg || "Unknown error",
|
|
128
|
-
apiError: response.data
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
this.accessToken = response.data.tenant_access_token;
|
|
132
|
-
this.tokenExpireTime = Date.now() + Math.min(response.data.expire * 1000, this.MAX_TOKEN_LIFETIME);
|
|
133
|
-
Logger.log(`成功获取新的访问令牌,有效期: ${response.data.expire} 秒`);
|
|
134
|
-
return this.accessToken; // 使用类型断言确保返回类型为string
|
|
135
|
-
}
|
|
136
|
-
catch (error) {
|
|
137
|
-
if (error instanceof AxiosError && error.response) {
|
|
138
|
-
Logger.error(`获取访问令牌请求失败:`);
|
|
139
|
-
Logger.error(`状态码: ${error.response.status}`);
|
|
140
|
-
Logger.error(`响应头: ${JSON.stringify(error.response.headers, null, 2)}`);
|
|
141
|
-
Logger.error(`响应数据: ${JSON.stringify(error.response.data, null, 2)}`);
|
|
142
|
-
throw {
|
|
143
|
-
status: error.response.status,
|
|
144
|
-
err: error.response.data?.msg || "Unknown error",
|
|
145
|
-
apiError: error.response.data
|
|
146
|
-
};
|
|
147
|
-
}
|
|
148
|
-
Logger.error('获取访问令牌时发生未知错误:', error);
|
|
149
|
-
throw new Error("Failed to get Feishu access token");
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
async request(endpoint, method = "GET", data) {
|
|
153
|
-
try {
|
|
154
|
-
const accessToken = await this.getAccessToken();
|
|
155
|
-
const url = `${this.baseUrl}${endpoint}`;
|
|
156
|
-
const headers = {
|
|
157
|
-
Authorization: `Bearer ${accessToken}`,
|
|
158
|
-
'Content-Type': 'application/json',
|
|
159
|
-
};
|
|
160
|
-
Logger.log('准备发送请求:');
|
|
161
|
-
Logger.log(`请求URL: ${url}`);
|
|
162
|
-
Logger.log(`请求方法: ${method}`);
|
|
163
|
-
Logger.log(`请求头: ${JSON.stringify(headers, null, 2)}`);
|
|
164
|
-
if (data) {
|
|
165
|
-
Logger.log(`请求数据: ${JSON.stringify(data, null, 2)}`);
|
|
166
|
-
}
|
|
167
|
-
const response = await axios({
|
|
168
|
-
method,
|
|
169
|
-
url,
|
|
170
|
-
headers,
|
|
171
|
-
data,
|
|
172
|
-
});
|
|
173
|
-
Logger.log('收到响应:');
|
|
174
|
-
Logger.log(`响应状态码: ${response.status}`);
|
|
175
|
-
Logger.log(`响应头: ${JSON.stringify(response.headers, null, 2)}`);
|
|
176
|
-
Logger.log(`响应数据: ${JSON.stringify(response.data, null, 2)}`);
|
|
177
|
-
// 处理飞书API的错误响应(非零code)
|
|
178
|
-
if (response.data && typeof response.data.code === 'number' && response.data.code !== 0) {
|
|
179
|
-
Logger.error(`飞书API返回错误码: ${response.data.code}, 错误消息: ${response.data.msg}`);
|
|
180
|
-
// 构建规范的错误对象
|
|
181
|
-
throw {
|
|
182
|
-
status: response.status,
|
|
183
|
-
err: response.data.msg || "API返回错误码",
|
|
184
|
-
apiError: response.data
|
|
185
|
-
};
|
|
186
|
-
}
|
|
187
|
-
return response.data;
|
|
188
|
-
}
|
|
189
|
-
catch (error) {
|
|
190
|
-
if (error instanceof AxiosError && error.response) {
|
|
191
|
-
// HTTP错误响应
|
|
192
|
-
Logger.error(`请求失败:`);
|
|
193
|
-
Logger.error(`状态码: ${error.response.status}`);
|
|
194
|
-
Logger.error(`响应头: ${JSON.stringify(error.response.headers, null, 2)}`);
|
|
195
|
-
Logger.error(`响应数据: ${JSON.stringify(error.response.data, null, 2)}`);
|
|
196
|
-
// 飞书API错误响应处理
|
|
197
|
-
if (error.response.data && typeof error.response.data === 'object') {
|
|
198
|
-
const apiError = error.response.data;
|
|
199
|
-
throw {
|
|
200
|
-
status: error.response.status,
|
|
201
|
-
err: apiError.msg || "API请求失败",
|
|
202
|
-
apiError: apiError
|
|
203
|
-
};
|
|
204
|
-
}
|
|
205
|
-
// 通用HTTP错误
|
|
206
|
-
throw {
|
|
207
|
-
status: error.response.status,
|
|
208
|
-
err: error.response.data?.msg || "API请求失败",
|
|
209
|
-
apiError: error.response.data
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
// 处理预先构建的错误对象(例如从飞书API接收的非零code)
|
|
213
|
-
if (error && typeof error === 'object' && 'status' in error && 'err' in error) {
|
|
214
|
-
throw error;
|
|
215
|
-
}
|
|
216
|
-
// 其他未捕获的错误
|
|
217
|
-
Logger.error('发送请求时发生未知错误:', error);
|
|
218
|
-
throw {
|
|
219
|
-
status: 500,
|
|
220
|
-
err: error instanceof Error ? error.message : "未知错误",
|
|
221
|
-
apiError: error
|
|
222
|
-
};
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
// 创建新文档
|
|
226
|
-
async createDocument(title, folderToken) {
|
|
227
|
-
try {
|
|
228
|
-
Logger.log(`开始创建飞书文档,标题: ${title}${folderToken ? `,文件夹Token: ${folderToken}` : ',根目录'}`);
|
|
229
|
-
const endpoint = '/docx/v1/documents';
|
|
230
|
-
const data = {
|
|
231
|
-
title: title,
|
|
232
|
-
};
|
|
233
|
-
if (folderToken) {
|
|
234
|
-
data.folder_token = folderToken;
|
|
235
|
-
}
|
|
236
|
-
Logger.log(`准备请求API端点: ${endpoint}`);
|
|
237
|
-
Logger.log(`请求数据: ${JSON.stringify(data, null, 2)}`);
|
|
238
|
-
const response = await this.request(endpoint, 'POST', data);
|
|
239
|
-
const docInfo = response.data?.document;
|
|
240
|
-
Logger.log(`文档创建成功,文档ID: ${docInfo?.document_id}`);
|
|
241
|
-
Logger.log(`文档详情: ${JSON.stringify(docInfo, null, 2)}`);
|
|
242
|
-
return docInfo;
|
|
243
|
-
}
|
|
244
|
-
catch (error) {
|
|
245
|
-
this.wrapAndThrowError('创建文档失败', error);
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
// 获取文档信息
|
|
249
|
-
async getDocumentInfo(documentId) {
|
|
250
|
-
try {
|
|
251
|
-
Logger.log(`获取文档信息,原始文档ID/URL: ${documentId}`);
|
|
252
|
-
// 使用工具函数提取文档ID
|
|
253
|
-
const extractedDocId = normalizeDocumentId(documentId);
|
|
254
|
-
Logger.log(`提取的文档ID: ${extractedDocId}`);
|
|
255
|
-
const endpoint = `/docx/v1/documents/${extractedDocId}`;
|
|
256
|
-
const response = await this.request(endpoint);
|
|
257
|
-
if (!response || !response.data) {
|
|
258
|
-
this.wrapAndThrowError('获取文档信息失败,无效的响应', response);
|
|
259
|
-
}
|
|
260
|
-
Logger.log('文档信息获取成功');
|
|
261
|
-
return response.data;
|
|
262
|
-
}
|
|
263
|
-
catch (error) {
|
|
264
|
-
this.wrapAndThrowError('获取文档信息失败', error);
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
// 获取文档纯文本内容
|
|
268
|
-
async getDocumentContent(documentId, lang = 0) {
|
|
269
|
-
try {
|
|
270
|
-
Logger.log(`获取文档内容,原始文档ID/URL: ${documentId}`);
|
|
271
|
-
// 使用工具函数提取文档ID
|
|
272
|
-
const extractedDocId = normalizeDocumentId(documentId);
|
|
273
|
-
Logger.log(`提取的文档ID: ${extractedDocId}`);
|
|
274
|
-
const endpoint = `/docx/v1/documents/${extractedDocId}/raw_content`;
|
|
275
|
-
const params = lang > 0 ? `?lang=${lang}` : '';
|
|
276
|
-
const response = await this.request(`${endpoint}${params}`);
|
|
277
|
-
if (!response || !response.data) {
|
|
278
|
-
this.wrapAndThrowError('获取文档内容失败,无效的响应', response);
|
|
279
|
-
}
|
|
280
|
-
Logger.log('文档内容获取成功');
|
|
281
|
-
return response.data.content || '';
|
|
282
|
-
}
|
|
283
|
-
catch (error) {
|
|
284
|
-
this.wrapAndThrowError('获取文档内容失败', error);
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
// 获取文档块
|
|
288
|
-
async getDocumentBlocks(documentId, pageSize = 500) {
|
|
289
|
-
try {
|
|
290
|
-
Logger.log(`获取文档块结构,原始文档ID/URL: ${documentId}`);
|
|
291
|
-
// 使用工具函数提取文档ID
|
|
292
|
-
const extractedDocId = normalizeDocumentId(documentId);
|
|
293
|
-
Logger.log(`提取的文档ID: ${extractedDocId},页大小: ${pageSize}`);
|
|
294
|
-
const endpoint = `/docx/v1/documents/${extractedDocId}/blocks`;
|
|
295
|
-
const params = `?document_revision_id=-1&page_size=${pageSize}`;
|
|
296
|
-
const response = await this.request(`${endpoint}${params}`);
|
|
297
|
-
if (!response || !response.data || !response.data.items) {
|
|
298
|
-
this.wrapAndThrowError('获取文档块结构失败,无效的响应', response);
|
|
299
|
-
}
|
|
300
|
-
Logger.log(`文档块结构获取成功,共获取${response.data.items.length}个块`);
|
|
301
|
-
return response.data.items;
|
|
302
|
-
}
|
|
303
|
-
catch (error) {
|
|
304
|
-
this.wrapAndThrowError('获取文档块结构失败', error);
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
// 创建代码块
|
|
308
|
-
async createCodeBlock(documentId, parentBlockId, code, language = 0, wrap = false, index = 0) {
|
|
309
|
-
try {
|
|
310
|
-
// 确保语言参数不为0,默认使用1(PlainText)
|
|
311
|
-
const safeLanguage = language === 0 ? 1 : language;
|
|
312
|
-
Logger.log(`创建代码块,原始文档ID/URL: ${documentId}`);
|
|
313
|
-
Logger.log(`语言: ${safeLanguage},自动换行: ${wrap},插入位置: ${index}`);
|
|
314
|
-
const blockContent = createCodeBlockContent(code, safeLanguage, wrap);
|
|
315
|
-
return await this.createDocumentBlock(documentId, parentBlockId, blockContent, index);
|
|
316
|
-
}
|
|
317
|
-
catch (error) {
|
|
318
|
-
this.wrapAndThrowError('创建代码块失败', error);
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
// 创建文本块
|
|
322
|
-
async createTextBlock(documentId, parentBlockId, textContents, align = 1, index = 0) {
|
|
323
|
-
try {
|
|
324
|
-
Logger.log(`创建文本块,原始文档ID/URL: ${documentId}`);
|
|
325
|
-
Logger.log(`对齐方式: ${align},插入位置: ${index}`);
|
|
326
|
-
const blockContent = createTextBlockContent(textContents, align);
|
|
327
|
-
return await this.createDocumentBlock(documentId, parentBlockId, blockContent, index);
|
|
328
|
-
}
|
|
329
|
-
catch (error) {
|
|
330
|
-
this.wrapAndThrowError('创建文本块失败', error);
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
// 创建文档块
|
|
334
|
-
async createDocumentBlock(documentId, parentBlockId, blockContent, index = 0) {
|
|
335
|
-
try {
|
|
336
|
-
Logger.log(`创建文档块,原始文档ID/URL: ${documentId}`);
|
|
337
|
-
// 使用工具函数提取文档ID
|
|
338
|
-
const extractedDocId = normalizeDocumentId(documentId);
|
|
339
|
-
Logger.log(`提取的文档ID: ${extractedDocId},父块ID: ${parentBlockId},插入位置: ${index}`);
|
|
340
|
-
const endpoint = `/docx/v1/documents/${extractedDocId}/blocks/${parentBlockId}/children`;
|
|
341
|
-
const params = `?document_revision_id=-1`;
|
|
342
|
-
const data = {
|
|
343
|
-
children: [blockContent],
|
|
344
|
-
index: index
|
|
345
|
-
};
|
|
346
|
-
const response = await this.request(endpoint + params, 'POST', data);
|
|
347
|
-
if (!response || !response.data) {
|
|
348
|
-
this.wrapAndThrowError('创建文档块失败,无效的响应', response);
|
|
349
|
-
}
|
|
350
|
-
Logger.log('文档块创建成功');
|
|
351
|
-
return response.data;
|
|
352
|
-
}
|
|
353
|
-
catch (error) {
|
|
354
|
-
this.wrapAndThrowError('创建文档块失败', error);
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
// 创建标题块
|
|
358
|
-
async createHeadingBlock(documentId, parentBlockId, text, level = 1, index = 0, align = 1) {
|
|
359
|
-
try {
|
|
360
|
-
Logger.log(`创建标题块,原始文档ID/URL: ${documentId}`);
|
|
361
|
-
Logger.log(`标题级别: ${level},对齐方式: ${align},插入位置: ${index}`);
|
|
362
|
-
const blockContent = createHeadingBlockContent(text, level, align);
|
|
363
|
-
return await this.createDocumentBlock(documentId, parentBlockId, blockContent, index);
|
|
364
|
-
}
|
|
365
|
-
catch (error) {
|
|
366
|
-
this.wrapAndThrowError('创建标题块失败', error);
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
// 创建列表块
|
|
370
|
-
async createListBlock(documentId, parentBlockId, text, isOrdered = false, index = 0, align = 1) {
|
|
371
|
-
try {
|
|
372
|
-
Logger.log(`创建列表块,原始文档ID/URL: ${documentId}`);
|
|
373
|
-
Logger.log(`有序列表: ${isOrdered},对齐方式: ${align},插入位置: ${index}`);
|
|
374
|
-
const blockContent = createListBlockContent(text, isOrdered, align);
|
|
375
|
-
return await this.createDocumentBlock(documentId, parentBlockId, blockContent, index);
|
|
376
|
-
}
|
|
377
|
-
catch (error) {
|
|
378
|
-
this.wrapAndThrowError('创建列表块失败', error);
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
// 批量创建文档块
|
|
382
|
-
async createDocumentBlocks(documentId, parentBlockId, blockContents, index = 0) {
|
|
383
|
-
try {
|
|
384
|
-
Logger.log(`批量创建文档块,原始文档ID/URL: ${documentId}`);
|
|
385
|
-
// 使用工具函数提取文档ID
|
|
386
|
-
const extractedDocId = normalizeDocumentId(documentId);
|
|
387
|
-
Logger.log(`提取的文档ID: ${extractedDocId},父块ID: ${parentBlockId},块数量: ${blockContents.length},插入位置: ${index}`);
|
|
388
|
-
// 飞书API没有批量创建的单独接口,使用常规块创建接口
|
|
389
|
-
const endpoint = `/docx/v1/documents/${extractedDocId}/blocks/${parentBlockId}/children`;
|
|
390
|
-
const params = `?document_revision_id=-1`;
|
|
391
|
-
const data = {
|
|
392
|
-
children: blockContents,
|
|
393
|
-
index: index
|
|
394
|
-
};
|
|
395
|
-
const response = await this.request(endpoint + params, 'POST', data);
|
|
396
|
-
if (!response || !response.data) {
|
|
397
|
-
this.wrapAndThrowError('批量创建文档块失败,无效的响应', response);
|
|
398
|
-
}
|
|
399
|
-
Logger.log(`批量创建文档块成功,创建了${blockContents.length}个块`);
|
|
400
|
-
return response.data;
|
|
401
|
-
}
|
|
402
|
-
catch (error) {
|
|
403
|
-
this.wrapAndThrowError('批量创建文档块失败', error);
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
// 获取块内容
|
|
407
|
-
async getBlockContent(documentId, blockId) {
|
|
408
|
-
try {
|
|
409
|
-
Logger.log(`获取块内容,原始文档ID/URL: ${documentId}`);
|
|
410
|
-
// 使用工具函数提取文档ID
|
|
411
|
-
const extractedDocId = normalizeDocumentId(documentId);
|
|
412
|
-
Logger.log(`提取的文档ID: ${extractedDocId},块ID: ${blockId}`);
|
|
413
|
-
const endpoint = `/docx/v1/documents/${extractedDocId}/blocks/${blockId}`;
|
|
414
|
-
const params = `?document_revision_id=-1`;
|
|
415
|
-
const response = await this.request(endpoint + params);
|
|
416
|
-
if (!response || !response.data) {
|
|
417
|
-
this.wrapAndThrowError('获取块内容失败,无效的响应', response);
|
|
418
|
-
}
|
|
419
|
-
Logger.log('块内容获取成功');
|
|
420
|
-
return response.data;
|
|
421
|
-
}
|
|
422
|
-
catch (error) {
|
|
423
|
-
this.wrapAndThrowError('获取块内容失败', error);
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
// 更新块文本内容
|
|
427
|
-
async updateBlockTextContent(documentId, blockId, textElements) {
|
|
428
|
-
try {
|
|
429
|
-
const docId = normalizeDocumentId(documentId);
|
|
430
|
-
if (!docId) {
|
|
431
|
-
throw new Error(`无效的文档ID: ${documentId}`);
|
|
432
|
-
}
|
|
433
|
-
Logger.log(`开始更新块文本内容,文档ID: ${docId},块ID: ${blockId}`);
|
|
434
|
-
const endpoint = `/docx/v1/documents/${docId}/blocks/${blockId}?document_revision_id=-1`;
|
|
435
|
-
Logger.log(`准备请求API端点: ${endpoint}`);
|
|
436
|
-
const elements = textElements.map(item => ({
|
|
437
|
-
text_run: {
|
|
438
|
-
content: item.text,
|
|
439
|
-
text_element_style: item.style || {}
|
|
440
|
-
}
|
|
441
|
-
}));
|
|
442
|
-
const data = {
|
|
443
|
-
update_text_elements: {
|
|
444
|
-
elements: elements
|
|
445
|
-
}
|
|
446
|
-
};
|
|
447
|
-
Logger.log(`请求数据: ${JSON.stringify(data, null, 2)}`);
|
|
448
|
-
const response = await this.request(endpoint, 'PATCH', data);
|
|
449
|
-
Logger.log(`块文本内容更新成功: ${JSON.stringify(response.data, null, 2)}`);
|
|
450
|
-
return response.data;
|
|
451
|
-
}
|
|
452
|
-
catch (error) {
|
|
453
|
-
this.wrapAndThrowError(`更新块文本内容失败`, error);
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
// 获取Wiki节点信息并提取文档ID
|
|
457
|
-
async getWikiNodeInfo(wikiToken) {
|
|
458
|
-
try {
|
|
459
|
-
Logger.log(`获取Wiki节点信息,原始Wiki Token/URL: ${wikiToken}`);
|
|
460
|
-
// 使用工具函数提取Wiki Token
|
|
461
|
-
const extractedToken = normalizeWikiToken(wikiToken);
|
|
462
|
-
Logger.log(`提取的Wiki Token: ${extractedToken}`);
|
|
463
|
-
const endpoint = `/wiki/v2/spaces/get_node?token=${extractedToken}`;
|
|
464
|
-
const response = await this.request(endpoint);
|
|
465
|
-
if (!response || !response.data) {
|
|
466
|
-
this.wrapAndThrowError('获取Wiki节点信息失败,无效的响应', response);
|
|
467
|
-
}
|
|
468
|
-
Logger.log('Wiki节点信息获取成功');
|
|
469
|
-
return response.data;
|
|
470
|
-
}
|
|
471
|
-
catch (error) {
|
|
472
|
-
this.wrapAndThrowError('获取Wiki节点信息失败', error);
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
}
|