@yanhaidao/wecom 2.3.14 → 2.3.150
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/README.md +15 -110
- package/changelog/v2.3.15.md +15 -0
- package/docs/update-content-fix.md +135 -0
- package/package.json +1 -1
- package/src/agent/handler.ts +6 -6
- package/src/capability/doc/client.ts +326 -25
- package/src/capability/doc/schema.ts +166 -50
- package/src/capability/doc/tool.ts +250 -125
- package/src/capability/doc/types.ts +268 -0
- package/src/outbound.ts +12 -7
- package/src/runtime.ts +1 -0
- package/src/target.ts +37 -29
|
@@ -20,7 +20,7 @@ function mapDocTypeLabel(docType: number): string {
|
|
|
20
20
|
function summarizeDocInfo(info: any = {}) {
|
|
21
21
|
const docName = readString(info.doc_name) || "未命名文档";
|
|
22
22
|
const docType = mapDocTypeLabel(Number(info.doc_type));
|
|
23
|
-
return `${docType}
|
|
23
|
+
return `${docType}"${docName}"信息已获取`;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
function summarizeDocAuth(result: any = {}) {
|
|
@@ -77,7 +77,7 @@ function buildDocAuthDiagnosis(result: any = {}, requesterSenderId = "") {
|
|
|
77
77
|
];
|
|
78
78
|
const recommendations: string[] = [];
|
|
79
79
|
if (likelyAnonymousLinkFailure) {
|
|
80
|
-
recommendations.push("
|
|
80
|
+
recommendations.push('当前更像是仅企业内可访问;匿名浏览器或未登录企业微信环境通常会显示"文档不存在"。');
|
|
81
81
|
}
|
|
82
82
|
if (requester) {
|
|
83
83
|
if (requesterIsCollaborator) {
|
|
@@ -176,7 +176,7 @@ function buildShareLinkDiagnosis(params: { shareUrl: string; finalUrl: string; s
|
|
|
176
176
|
];
|
|
177
177
|
const recommendations: string[] = [];
|
|
178
178
|
if (likelyUnavailableToGuest) {
|
|
179
|
-
recommendations.push(
|
|
179
|
+
recommendations.push('当前链接对 guest/未登录企业微信环境返回 blankpage,外部访问会表现为打不开或像"文档不存在"。');
|
|
180
180
|
}
|
|
181
181
|
if (shareCode) {
|
|
182
182
|
recommendations.push(`当前链接带有分享码 scode=${shareCode}。如分享码过期或未生效,外部访问会失败。`);
|
|
@@ -269,7 +269,7 @@ function summarizeDocAccess(result: any = {}) {
|
|
|
269
269
|
|
|
270
270
|
function summarizeFormInfo(result: any = {}) {
|
|
271
271
|
const title = readString(result.formInfo?.form_title) || "未命名收集表";
|
|
272
|
-
return
|
|
272
|
+
return `收集表"${title}"信息已获取`;
|
|
273
273
|
}
|
|
274
274
|
|
|
275
275
|
function summarizeFormAnswer(result: any = {}) {
|
|
@@ -346,7 +346,7 @@ export function registerWecomDocTools(api: OpenClawPluginApi) {
|
|
|
346
346
|
const action = params.action;
|
|
347
347
|
switch (action) {
|
|
348
348
|
case "create": {
|
|
349
|
-
const
|
|
349
|
+
const explicitCollaborators = Array.isArray(params.collaborators) ? [...params.collaborators] : [];
|
|
350
350
|
const result = await docClient.createDoc({
|
|
351
351
|
agent: account,
|
|
352
352
|
docName: params.docName,
|
|
@@ -356,122 +356,215 @@ export function registerWecomDocTools(api: OpenClawPluginApi) {
|
|
|
356
356
|
adminUsers: params.adminUsers,
|
|
357
357
|
});
|
|
358
358
|
|
|
359
|
+
// Auto-set security rules for better default permissions (internal users can edit)
|
|
360
|
+
try {
|
|
361
|
+
await docClient.setDocJoinRule({
|
|
362
|
+
agent: account,
|
|
363
|
+
docId: result.docId,
|
|
364
|
+
request: {
|
|
365
|
+
enable_corp_internal: true,
|
|
366
|
+
corp_internal_auth: 2, // 2 = edit permission
|
|
367
|
+
enable_corp_external: false,
|
|
368
|
+
ban_share_external: false,
|
|
369
|
+
},
|
|
370
|
+
});
|
|
371
|
+
} catch (err) {
|
|
372
|
+
// Non-fatal: document created, just default permissions may be read-only
|
|
373
|
+
}
|
|
374
|
+
|
|
359
375
|
// Handle initial content (title/body separation) if provided
|
|
376
|
+
// Supports: string (text) or {type: "text"|"image", content/url: string}
|
|
360
377
|
let contentResult: any = null;
|
|
361
378
|
if (Array.isArray(params.init_content) && params.init_content.length > 0) {
|
|
362
379
|
try {
|
|
363
|
-
//
|
|
364
|
-
const
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
//
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
380
|
+
// Helper: check if content item is an image
|
|
381
|
+
const isImageItem = (item: any): boolean => {
|
|
382
|
+
if (typeof item === "object" && item !== null) {
|
|
383
|
+
return item.type === "image" || (item.url && !item.content);
|
|
384
|
+
}
|
|
385
|
+
if (typeof item === "string") {
|
|
386
|
+
// Detect image URLs
|
|
387
|
+
return item.startsWith("http") &&
|
|
388
|
+
(item.includes(".png") || item.includes(".jpg") ||
|
|
389
|
+
item.includes(".jpeg") || item.includes(".gif") ||
|
|
390
|
+
item.includes("qpic.cn") || item.includes("weixin.qq.com"));
|
|
391
|
+
}
|
|
392
|
+
return false;
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
// Helper: get image URL from content item
|
|
396
|
+
const getImageUrl = (item: any): string => {
|
|
397
|
+
if (typeof item === "object" && item !== null) {
|
|
398
|
+
return item.url || item.content || "";
|
|
399
|
+
}
|
|
400
|
+
return String(item);
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
// Helper: download image and convert to base64
|
|
404
|
+
const downloadImageAsBase64 = async (url: string): Promise<string> => {
|
|
405
|
+
const response = await fetch(url);
|
|
406
|
+
if (!response.ok) {
|
|
407
|
+
throw new Error(`Failed to download image: ${url}`);
|
|
408
|
+
}
|
|
409
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
410
|
+
return Buffer.from(arrayBuffer).toString("base64");
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
// Helper: get text from content item
|
|
414
|
+
const getText = (item: any): string => {
|
|
415
|
+
if (typeof item === "object" && item !== null) {
|
|
416
|
+
return item.content || item.text || "";
|
|
417
|
+
}
|
|
418
|
+
return String(item);
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
// Step 1: Insert first paragraph (title) at index 0
|
|
388
422
|
if (params.init_content[0]) {
|
|
389
|
-
const
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
423
|
+
const firstItem = params.init_content[0];
|
|
424
|
+
if (isImageItem(firstItem)) {
|
|
425
|
+
// First item is image - upload first, then insert at index 0
|
|
426
|
+
const imgUrl = getImageUrl(firstItem);
|
|
427
|
+
|
|
428
|
+
try {
|
|
429
|
+
// Upload image to WeCom to get proper image_id
|
|
430
|
+
const base64 = await downloadImageAsBase64(imgUrl);
|
|
431
|
+
const uploadResult = await docClient.uploadDocImage({
|
|
432
|
+
agent: account,
|
|
433
|
+
docId: result.docId,
|
|
434
|
+
base64_content: base64,
|
|
435
|
+
});
|
|
400
436
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
437
|
+
// Insert image using uploaded URL
|
|
438
|
+
// Note: version is optional, API handles concurrency
|
|
439
|
+
await docClient.updateDocContent({
|
|
440
|
+
agent: account,
|
|
441
|
+
docId: result.docId,
|
|
442
|
+
requests: [{
|
|
443
|
+
insert_image: {
|
|
444
|
+
image_id: uploadResult.url,
|
|
445
|
+
location: { index: 0 },
|
|
446
|
+
width: uploadResult.width,
|
|
447
|
+
height: uploadResult.height
|
|
448
|
+
}
|
|
449
|
+
}]
|
|
450
|
+
});
|
|
451
|
+
} catch (uploadErr) {
|
|
452
|
+
console.error(`Failed to upload first image ${imgUrl}:`, uploadErr);
|
|
453
|
+
throw new Error(`First image upload failed: ${uploadErr instanceof Error ? uploadErr.message : String(uploadErr)}`);
|
|
454
|
+
}
|
|
455
|
+
} else {
|
|
456
|
+
const titleText = getText(firstItem);
|
|
404
457
|
await docClient.updateDocContent({
|
|
405
458
|
agent: account,
|
|
406
459
|
docId: result.docId,
|
|
407
460
|
requests: [{
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
461
|
+
insert_text: {
|
|
462
|
+
text: titleText,
|
|
463
|
+
location: { index: 0 }
|
|
411
464
|
}
|
|
412
465
|
}]
|
|
413
466
|
});
|
|
467
|
+
|
|
468
|
+
// Apply Title Styling (Bold)
|
|
469
|
+
if (titleText.length > 0) {
|
|
470
|
+
await docClient.updateDocContent({
|
|
471
|
+
agent: account,
|
|
472
|
+
docId: result.docId,
|
|
473
|
+
requests: [{
|
|
474
|
+
update_text_property: {
|
|
475
|
+
text_property: { bold: true },
|
|
476
|
+
ranges: [{ start_index: 0, length: titleText.length }]
|
|
477
|
+
}
|
|
478
|
+
}]
|
|
479
|
+
});
|
|
480
|
+
}
|
|
414
481
|
}
|
|
415
482
|
}
|
|
416
483
|
|
|
417
|
-
// Step 2: For subsequent
|
|
484
|
+
// Step 2: For subsequent items, append with proper paragraph handling
|
|
485
|
+
// Per API spec: must get latest version and index before each batch_update
|
|
418
486
|
for (let i = 1; i < params.init_content.length; i++) {
|
|
419
|
-
const
|
|
420
|
-
if (!text) continue;
|
|
487
|
+
const item = params.init_content[i];
|
|
421
488
|
|
|
422
|
-
// Refresh content to get latest
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
docId: result.docId,
|
|
426
|
-
});
|
|
427
|
-
|
|
428
|
-
// Find the end of the document (or last paragraph)
|
|
429
|
-
// We use 'end' directly as the insertion point for appending.
|
|
430
|
-
// Note: WeCom 'end' is exclusive [begin, end).
|
|
431
|
-
// If we insert at 'end', we append after the last element.
|
|
432
|
-
let docEndIndex = currentContent.document.end;
|
|
433
|
-
|
|
434
|
-
// Safety adjustment: If the document has a final mandatory newline/EOF that we can't append after,
|
|
435
|
-
// we might need to insert *before* it.
|
|
436
|
-
// However, creating a NEW paragraph usually happens at the end.
|
|
437
|
-
// If we are unsure, we try 'end - 1' if 'end' fails, but 'end' is the standard "append" index.
|
|
438
|
-
// Given the user analysis "Paragraph 2 (5-117)" where 5 was the end of Para 1,
|
|
439
|
-
// it suggests we insert AT the boundary.
|
|
440
|
-
|
|
441
|
-
// We use insert_paragraph to create a split
|
|
442
|
-
await docClient.updateDocContent({
|
|
443
|
-
agent: account,
|
|
444
|
-
docId: result.docId,
|
|
445
|
-
requests: [{
|
|
446
|
-
insert_paragraph: {
|
|
447
|
-
location: { index: docEndIndex }
|
|
448
|
-
}
|
|
449
|
-
}]
|
|
450
|
-
});
|
|
451
|
-
|
|
452
|
-
// Now insert text into the new paragraph
|
|
453
|
-
// We need to refresh again or assume index shifted by 1
|
|
454
|
-
currentContent = await docClient.getDocContent({
|
|
455
|
-
agent: account,
|
|
456
|
-
docId: result.docId,
|
|
457
|
-
});
|
|
458
|
-
|
|
459
|
-
// The new paragraph should be at the end.
|
|
460
|
-
// We want to insert text *into* this new paragraph.
|
|
461
|
-
// The insert_paragraph likely created a new Paragraph node.
|
|
462
|
-
// We insert at the new end (which is inside the new paragraph).
|
|
463
|
-
const newParaIndex = currentContent.document.end;
|
|
464
|
-
|
|
465
|
-
await docClient.updateDocContent({
|
|
489
|
+
// Refresh content to get latest document structure and version
|
|
490
|
+
// API requires: version difference ≤ 100 from latest
|
|
491
|
+
const currentContent = await docClient.getDocContent({
|
|
466
492
|
agent: account,
|
|
467
493
|
docId: result.docId,
|
|
468
|
-
requests: [{
|
|
469
|
-
insert_text: {
|
|
470
|
-
text: text,
|
|
471
|
-
location: { index: newParaIndex }
|
|
472
|
-
}
|
|
473
|
-
}]
|
|
474
494
|
});
|
|
495
|
+
|
|
496
|
+
// Get the end index of the document
|
|
497
|
+
const docEndIndex = currentContent.document.end;
|
|
498
|
+
const currentVersion = currentContent.version;
|
|
499
|
+
|
|
500
|
+
if (isImageItem(item)) {
|
|
501
|
+
// Insert image: upload first, then create paragraph, then insert image
|
|
502
|
+
const imgUrl = getImageUrl(item);
|
|
503
|
+
|
|
504
|
+
try {
|
|
505
|
+
// Step 1: Download and upload image to WeCom
|
|
506
|
+
const base64 = await downloadImageAsBase64(imgUrl);
|
|
507
|
+
const uploadResult = await docClient.uploadDocImage({
|
|
508
|
+
agent: account,
|
|
509
|
+
docId: result.docId,
|
|
510
|
+
base64_content: base64,
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
// Step 2: Create new paragraph and insert image in one batch (2 operations ≤ 30)
|
|
514
|
+
// Per API spec: all indices are based on the same document snapshot
|
|
515
|
+
// insert_paragraph at docEndIndex creates a new paragraph
|
|
516
|
+
// insert_image at docEndIndex + 1 inserts into the newly created paragraph
|
|
517
|
+
await docClient.updateDocContent({
|
|
518
|
+
agent: account,
|
|
519
|
+
docId: result.docId,
|
|
520
|
+
version: currentVersion, // Pass version for concurrency control
|
|
521
|
+
requests: [
|
|
522
|
+
{
|
|
523
|
+
insert_paragraph: {
|
|
524
|
+
location: { index: docEndIndex }
|
|
525
|
+
}
|
|
526
|
+
},
|
|
527
|
+
{
|
|
528
|
+
insert_image: {
|
|
529
|
+
image_id: uploadResult.url,
|
|
530
|
+
location: { index: docEndIndex + 1 },
|
|
531
|
+
width: uploadResult.width,
|
|
532
|
+
height: uploadResult.height
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
]
|
|
536
|
+
});
|
|
537
|
+
} catch (uploadErr) {
|
|
538
|
+
console.error(`Failed to upload image ${imgUrl}:`, uploadErr);
|
|
539
|
+
throw new Error(`Image upload failed: ${uploadErr instanceof Error ? uploadErr.message : String(uploadErr)}`);
|
|
540
|
+
}
|
|
541
|
+
} else {
|
|
542
|
+
const text = getText(item);
|
|
543
|
+
if (!text) continue;
|
|
544
|
+
|
|
545
|
+
// Insert text: create paragraph and insert text in one batch (2 operations ≤ 30)
|
|
546
|
+
// Per API spec: all indices are based on the same document snapshot
|
|
547
|
+
// insert_paragraph at docEndIndex creates a new paragraph
|
|
548
|
+
// insert_text at docEndIndex + 1 inserts into the newly created paragraph
|
|
549
|
+
await docClient.updateDocContent({
|
|
550
|
+
agent: account,
|
|
551
|
+
docId: result.docId,
|
|
552
|
+
version: currentVersion, // Pass version for concurrency control
|
|
553
|
+
requests: [
|
|
554
|
+
{
|
|
555
|
+
insert_paragraph: {
|
|
556
|
+
location: { index: docEndIndex }
|
|
557
|
+
}
|
|
558
|
+
},
|
|
559
|
+
{
|
|
560
|
+
insert_text: {
|
|
561
|
+
text: text,
|
|
562
|
+
location: { index: docEndIndex + 1 }
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
]
|
|
566
|
+
});
|
|
567
|
+
}
|
|
475
568
|
}
|
|
476
569
|
contentResult = "init_content_populated";
|
|
477
570
|
} catch (err) {
|
|
@@ -480,13 +573,13 @@ export function registerWecomDocTools(api: OpenClawPluginApi) {
|
|
|
480
573
|
}
|
|
481
574
|
|
|
482
575
|
let accessResult: any = null;
|
|
483
|
-
if ((Array.isArray(params.viewers) && params.viewers.length > 0) ||
|
|
576
|
+
if ((Array.isArray(params.viewers) && params.viewers.length > 0) || explicitCollaborators.length > 0) {
|
|
484
577
|
try {
|
|
485
578
|
accessResult = await docClient.grantDocAccess({
|
|
486
579
|
agent: account,
|
|
487
580
|
docId: result.docId,
|
|
488
581
|
viewers: params.viewers,
|
|
489
|
-
collaborators,
|
|
582
|
+
collaborators: explicitCollaborators,
|
|
490
583
|
});
|
|
491
584
|
} catch (err) {
|
|
492
585
|
return buildToolResult({
|
|
@@ -499,7 +592,7 @@ export function registerWecomDocTools(api: OpenClawPluginApi) {
|
|
|
499
592
|
docId: result.docId,
|
|
500
593
|
title: readString(params.docName),
|
|
501
594
|
url: result.url || undefined,
|
|
502
|
-
summary: `已创建${mapDocTypeLabel(result.docType)}
|
|
595
|
+
summary: `已创建${mapDocTypeLabel(result.docType)}"${readString(params.docName)}"(docId: ${result.docId}),但权限授予失败`,
|
|
503
596
|
usageHint: buildDocIdUsageHint(result.docId) || undefined,
|
|
504
597
|
error: err instanceof Error ? err.message : String(err),
|
|
505
598
|
raw: { create: result.raw },
|
|
@@ -516,8 +609,8 @@ export function registerWecomDocTools(api: OpenClawPluginApi) {
|
|
|
516
609
|
title: readString(params.docName),
|
|
517
610
|
url: result.url || undefined,
|
|
518
611
|
summary: accessResult
|
|
519
|
-
? `已创建${mapDocTypeLabel(result.docType)}
|
|
520
|
-
: `已创建${mapDocTypeLabel(result.docType)}
|
|
612
|
+
? `已创建${mapDocTypeLabel(result.docType)}"${readString(params.docName)}"(docId: ${result.docId});${summarizeDocAccess(accessResult)}` + (contentResult ? `;内容填充: ${contentResult}` : "")
|
|
613
|
+
: `已创建${mapDocTypeLabel(result.docType)}"${readString(params.docName)}"(docId: ${result.docId})` + (contentResult ? `;内容填充: ${contentResult}` : ""),
|
|
521
614
|
usageHint: buildDocIdUsageHint(result.docId) || undefined,
|
|
522
615
|
raw: accessResult ? { create: result.raw, access: accessResult.raw } : result.raw,
|
|
523
616
|
});
|
|
@@ -534,7 +627,7 @@ export function registerWecomDocTools(api: OpenClawPluginApi) {
|
|
|
534
627
|
accountId: account.accountId,
|
|
535
628
|
docId: result.docId,
|
|
536
629
|
title: result.newName,
|
|
537
|
-
summary:
|
|
630
|
+
summary: `文档已重命名为"${result.newName}"`,
|
|
538
631
|
raw: result.raw,
|
|
539
632
|
});
|
|
540
633
|
}
|
|
@@ -733,18 +826,22 @@ export function registerWecomDocTools(api: OpenClawPluginApi) {
|
|
|
733
826
|
});
|
|
734
827
|
}
|
|
735
828
|
case "update_content": {
|
|
829
|
+
const batchMode = params.batchMode === true;
|
|
830
|
+
|
|
736
831
|
const result = await docClient.updateDocContent({
|
|
737
832
|
agent: account,
|
|
738
833
|
docId: params.docId,
|
|
739
834
|
requests: params.requests,
|
|
740
835
|
version: params.version,
|
|
836
|
+
batchMode: batchMode,
|
|
741
837
|
});
|
|
838
|
+
|
|
742
839
|
return buildToolResult({
|
|
743
840
|
ok: true,
|
|
744
841
|
action: "update_content",
|
|
745
842
|
accountId: account.accountId,
|
|
746
843
|
docId: params.docId,
|
|
747
|
-
summary:
|
|
844
|
+
summary: `文档内容已更新(${batchMode ? '批量' : '顺序'}模式)`,
|
|
748
845
|
raw: result.raw,
|
|
749
846
|
});
|
|
750
847
|
}
|
|
@@ -811,22 +908,47 @@ export function registerWecomDocTools(api: OpenClawPluginApi) {
|
|
|
811
908
|
});
|
|
812
909
|
}
|
|
813
910
|
case "create_collect": {
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
911
|
+
// 创建收集表(表单)
|
|
912
|
+
// 参考 API 规范文档:E8_AF_B7_E4_B8_A5_E6_A0_BC_E6_8C_89_E7_85_A7_E4_BB_A5_E4_B8_---099c30ec-70bd-4e5b-ae03-212de0226a25.docx
|
|
913
|
+
try {
|
|
914
|
+
const result = await docClient.createCollect({
|
|
915
|
+
agent: account,
|
|
916
|
+
formInfo: params.formInfo,
|
|
917
|
+
spaceId: params.spaceId,
|
|
918
|
+
fatherId: params.fatherId,
|
|
919
|
+
});
|
|
920
|
+
const title = readString(result.title);
|
|
921
|
+
return buildToolResult({
|
|
922
|
+
ok: true,
|
|
923
|
+
action: "create_collect",
|
|
924
|
+
accountId: account.accountId,
|
|
925
|
+
formId: result.formId,
|
|
926
|
+
title: title || undefined,
|
|
927
|
+
summary: title ? `已创建收集表"${title}"(formId: ${result.formId})` : `已创建收集表(formId: ${result.formId})`,
|
|
928
|
+
raw: result.raw,
|
|
929
|
+
});
|
|
930
|
+
} catch (err) {
|
|
931
|
+
// 提供更详细的错误提示
|
|
932
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
933
|
+
const hint = `
|
|
934
|
+
创建收集表失败。请检查以下必填项:
|
|
935
|
+
- form_title: 收集表标题(必填)
|
|
936
|
+
- form_question.items: 问题数组(必填,≤200 个)
|
|
937
|
+
- 每个问题必须包含:question_id, title, pos, reply_type, must_reply
|
|
938
|
+
- 单选/多选/下拉列表必须提供 option_item 数组
|
|
939
|
+
- reply_type 对照表:1 文本,2 单选,3 多选,5 位置,9 图片,10 文件,11 日期,14 时间,15 下拉列表,16 体温,17 签名,18 部门,19 成员,22 时长
|
|
940
|
+
|
|
941
|
+
错误详情:${errorMsg}`;
|
|
942
|
+
return buildToolResult({
|
|
943
|
+
ok: false,
|
|
944
|
+
action: "create_collect",
|
|
945
|
+
accountId: account.accountId,
|
|
946
|
+
error: errorMsg,
|
|
947
|
+
summary: "创建收集表失败",
|
|
948
|
+
hint: hint.trim(),
|
|
949
|
+
raw: {},
|
|
950
|
+
});
|
|
951
|
+
}
|
|
830
952
|
}
|
|
831
953
|
case "modify_collect": {
|
|
832
954
|
const result = await docClient.modifyCollect({
|
|
@@ -843,7 +965,7 @@ export function registerWecomDocTools(api: OpenClawPluginApi) {
|
|
|
843
965
|
formId: result.formId,
|
|
844
966
|
title: title || undefined,
|
|
845
967
|
summary: title
|
|
846
|
-
? `收集表已更新(${result.oper}
|
|
968
|
+
? `收集表已更新(${result.oper}):"${title}"`
|
|
847
969
|
: `收集表已更新(${result.oper})`,
|
|
848
970
|
raw: result.raw,
|
|
849
971
|
});
|
|
@@ -909,7 +1031,10 @@ export function registerWecomDocTools(api: OpenClawPluginApi) {
|
|
|
909
1031
|
const result = await docClient.editSheetData({
|
|
910
1032
|
agent: account,
|
|
911
1033
|
docId: params.docId,
|
|
912
|
-
|
|
1034
|
+
sheetId: params.sheetId,
|
|
1035
|
+
startRow: params.startRow ?? 0,
|
|
1036
|
+
startColumn: params.startColumn ?? 0,
|
|
1037
|
+
gridData: params.gridData,
|
|
913
1038
|
});
|
|
914
1039
|
return buildToolResult({
|
|
915
1040
|
ok: true,
|