@yanhaidao/wecom 2.3.160 → 2.3.180
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 +235 -399
- package/SKILLS_CAL.md +895 -0
- package/SKILLS_DOC.md +2136 -0
- package/changelog/v2.3.18.md +22 -0
- package/index.ts +39 -3
- package/package.json +2 -3
- package/src/agent/handler.event-filter.test.ts +11 -0
- package/src/agent/handler.ts +732 -643
- package/src/app/account-runtime.ts +46 -20
- package/src/app/index.ts +19 -1
- package/src/capability/calendar/SKILLS_CHECKLIST.md +251 -0
- package/src/capability/calendar/client.ts +815 -0
- package/src/capability/calendar/index.ts +3 -0
- package/src/capability/calendar/schema.ts +417 -0
- package/src/capability/calendar/tool.ts +417 -0
- package/src/capability/calendar/types.ts +309 -0
- package/src/capability/doc/client.ts +567 -62
- package/src/capability/doc/schema.ts +419 -318
- package/src/capability/doc/tool.ts +1510 -1178
- package/src/capability/doc/types.ts +130 -14
- package/src/capability/mcp/index.ts +10 -0
- package/src/capability/mcp/schema.ts +107 -0
- package/src/capability/mcp/tool.ts +170 -0
- package/src/capability/mcp/transport.ts +394 -0
- package/src/channel.ts +70 -28
- package/src/config/schema.ts +71 -102
- package/src/outbound.test.ts +91 -14
- package/src/outbound.ts +143 -30
- package/src/runtime/reply-orchestrator.test.ts +35 -2
- package/src/runtime/reply-orchestrator.ts +14 -2
- package/src/runtime/session-manager.ts +20 -6
- package/src/runtime/source-registry.ts +165 -0
- package/src/transport/bot-ws/media.ts +269 -0
- package/src/transport/bot-ws/reply.test.ts +85 -17
- package/src/transport/bot-ws/reply.ts +109 -21
- package/src/transport/bot-ws/sdk-adapter.test.ts +64 -1
- package/src/transport/bot-ws/sdk-adapter.ts +88 -12
- package/.claude/settings.local.json +0 -11
- package/docs/update-content-fix.md +0 -135
|
@@ -391,19 +391,23 @@ export class WecomDocClient {
|
|
|
391
391
|
// Need to check current auth status
|
|
392
392
|
try {
|
|
393
393
|
const currentAuth = await this.getDocAuth({ agent, docId });
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
const newCollaboratorUserIds = normalizeDocMemberEntryList(collaborators)
|
|
400
|
-
.map(e => e.userid)
|
|
401
|
-
.filter(Boolean) as string[];
|
|
394
|
+
// Build a map of viewer entries with their full structure (preserving type and other fields)
|
|
395
|
+
const viewerMap = new Map<string, any>();
|
|
396
|
+
(currentAuth.docMembers || [])
|
|
397
|
+
.filter((m: any) => m.userid)
|
|
398
|
+
.forEach((m: any) => viewerMap.set(m.userid, m));
|
|
402
399
|
|
|
403
|
-
//
|
|
404
|
-
const
|
|
405
|
-
|
|
406
|
-
|
|
400
|
+
// Normalize new collaborators to get their userids
|
|
401
|
+
const newCollaboratorEntries = normalizeDocMemberEntryList(collaborators);
|
|
402
|
+
|
|
403
|
+
// Auto-add viewers who are being promoted to collaborators, preserving their original structure
|
|
404
|
+
const autoRemoveViewers = newCollaboratorEntries
|
|
405
|
+
.filter(entry => entry.userid && viewerMap.has(entry.userid))
|
|
406
|
+
.map(entry => {
|
|
407
|
+
// Preserve the original viewer's full structure (type, userid, etc.)
|
|
408
|
+
const originalViewer = viewerMap.get(entry.userid!);
|
|
409
|
+
return { ...originalViewer };
|
|
410
|
+
});
|
|
407
411
|
|
|
408
412
|
if (autoRemoveViewers.length > 0) {
|
|
409
413
|
finalRemoveViewers = autoRemoveViewers;
|
|
@@ -488,6 +492,16 @@ export class WecomDocClient {
|
|
|
488
492
|
throw new Error("问题数量不能超过 200 个");
|
|
489
493
|
}
|
|
490
494
|
|
|
495
|
+
// Auto-fill status fields for questions and options
|
|
496
|
+
questions.forEach((q: any) => {
|
|
497
|
+
if (q.status === undefined) q.status = 1;
|
|
498
|
+
if (Array.isArray(q.option_item)) {
|
|
499
|
+
q.option_item.forEach((opt: any) => {
|
|
500
|
+
if (opt.status === undefined) opt.status = 1;
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
});
|
|
504
|
+
|
|
491
505
|
// Validate each question
|
|
492
506
|
questions.forEach((q: any, index: number) => {
|
|
493
507
|
if (!q.question_id || !Number.isInteger(q.question_id) || q.question_id < 1) {
|
|
@@ -505,6 +519,9 @@ export class WecomDocClient {
|
|
|
505
519
|
if (q.must_reply === undefined || typeof q.must_reply !== 'boolean') {
|
|
506
520
|
throw new Error(`第${index + 1}个问题:must_reply 必填且必须为布尔值`);
|
|
507
521
|
}
|
|
522
|
+
if (q.status !== undefined && ![1, 2].includes(q.status)) {
|
|
523
|
+
throw new Error(`第${index + 1}个问题:status 必须为 1(正常) 或 2(删除)`);
|
|
524
|
+
}
|
|
508
525
|
|
|
509
526
|
// Validate option_item for single/multiple/dropdown questions
|
|
510
527
|
const requiresOptions = [2, 3, 15].includes(q.reply_type); // 单选/多选/下拉列表
|
|
@@ -520,6 +537,9 @@ export class WecomDocClient {
|
|
|
520
537
|
if (!opt.value || readString(opt.value).length === 0) {
|
|
521
538
|
throw new Error(`第${index + 1}个问题的第${optIndex + 1}个选项:value 必填`);
|
|
522
539
|
}
|
|
540
|
+
if (opt.status !== undefined && ![1, 2].includes(opt.status)) {
|
|
541
|
+
throw new Error(`第${index + 1}个问题的第${optIndex + 1}个选项:status 必须为 1(正常) 或 2(删除)`);
|
|
542
|
+
}
|
|
523
543
|
});
|
|
524
544
|
}
|
|
525
545
|
|
|
@@ -546,6 +566,13 @@ export class WecomDocClient {
|
|
|
546
566
|
console.warn("警告:timed_finish 与 timed_repeat_info 互斥,若都填优先定时重复");
|
|
547
567
|
}
|
|
548
568
|
|
|
569
|
+
// Validate timed_repeat_info.enable=true requires fill_in_range
|
|
570
|
+
if (formSetting.timed_repeat_info?.enable) {
|
|
571
|
+
if (!formSetting.fill_in_range || (!formSetting.fill_in_range.userids?.length && !formSetting.fill_in_range.departmentids?.length)) {
|
|
572
|
+
throw new Error("timed_repeat_info 开启时,fill_in_range 必填(需指定 userids 或 departmentids)");
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
|
|
549
576
|
// Build payload
|
|
550
577
|
const payload: Record<string, unknown> = {
|
|
551
578
|
form_info: {
|
|
@@ -563,8 +590,8 @@ export class WecomDocClient {
|
|
|
563
590
|
if (normalizedFatherId) payload.fatherid = normalizedFatherId;
|
|
564
591
|
|
|
565
592
|
const json = await this.postWecomDocApi({
|
|
566
|
-
path: "/cgi-bin/wedoc/
|
|
567
|
-
actionLabel: "
|
|
593
|
+
path: "/cgi-bin/wedoc/create_form",
|
|
594
|
+
actionLabel: "create_form",
|
|
568
595
|
agent,
|
|
569
596
|
body: payload,
|
|
570
597
|
});
|
|
@@ -605,6 +632,16 @@ export class WecomDocClient {
|
|
|
605
632
|
throw new Error("问题数量不能超过 200 个");
|
|
606
633
|
}
|
|
607
634
|
|
|
635
|
+
// Auto-fill status fields for questions and options
|
|
636
|
+
questions.forEach((q: any) => {
|
|
637
|
+
if (q.status === undefined) q.status = 1;
|
|
638
|
+
if (Array.isArray(q.option_item)) {
|
|
639
|
+
q.option_item.forEach((opt: any) => {
|
|
640
|
+
if (opt.status === undefined) opt.status = 1;
|
|
641
|
+
});
|
|
642
|
+
}
|
|
643
|
+
});
|
|
644
|
+
|
|
608
645
|
// Validate each question (same as createCollect)
|
|
609
646
|
questions.forEach((q: any, index: number) => {
|
|
610
647
|
if (!q.question_id || !Number.isInteger(q.question_id) || q.question_id < 1) {
|
|
@@ -658,8 +695,8 @@ export class WecomDocClient {
|
|
|
658
695
|
}
|
|
659
696
|
|
|
660
697
|
const json = await this.postWecomDocApi({
|
|
661
|
-
path: "/cgi-bin/wedoc/
|
|
662
|
-
actionLabel: "
|
|
698
|
+
path: "/cgi-bin/wedoc/modify_form",
|
|
699
|
+
actionLabel: "modify_form",
|
|
663
700
|
agent,
|
|
664
701
|
body: payload,
|
|
665
702
|
});
|
|
@@ -696,6 +733,12 @@ export class WecomDocClient {
|
|
|
696
733
|
.map((item) => Number(item))
|
|
697
734
|
.filter((item) => Number.isFinite(item))
|
|
698
735
|
: [];
|
|
736
|
+
|
|
737
|
+
// Official API limit: ≤100 answer IDs
|
|
738
|
+
if (normalizedAnswerIds.length > 100) {
|
|
739
|
+
throw new Error(`answer_ids 不能超过 100 个,当前:${normalizedAnswerIds.length}`);
|
|
740
|
+
}
|
|
741
|
+
|
|
699
742
|
const payload: Record<string, unknown> = {
|
|
700
743
|
repeated_id: normalizedRepeatedId,
|
|
701
744
|
};
|
|
@@ -724,6 +767,32 @@ export class WecomDocClient {
|
|
|
724
767
|
if (payload.length === 0) {
|
|
725
768
|
throw new Error("requests required");
|
|
726
769
|
}
|
|
770
|
+
|
|
771
|
+
// Validate each request per official API
|
|
772
|
+
payload.forEach((req: any, index: number) => {
|
|
773
|
+
const reqType = Number(req.req_type);
|
|
774
|
+
|
|
775
|
+
// req_type=2: Get submitted list - requires start_time and end_time (same day timestamps)
|
|
776
|
+
if (reqType === 2) {
|
|
777
|
+
if (!req.start_time || !req.end_time) {
|
|
778
|
+
throw new Error(`第${index + 1}个请求:req_type=2 时必须提供 start_time 和 end_time(当天时间戳)`);
|
|
779
|
+
}
|
|
780
|
+
// Validate timestamps are numbers
|
|
781
|
+
if (!Number.isFinite(Number(req.start_time)) || !Number.isFinite(Number(req.end_time))) {
|
|
782
|
+
throw new Error(`第${index + 1}个请求:start_time 和 end_time 必须是有效时间戳`);
|
|
783
|
+
}
|
|
784
|
+
// Validate end_time >= start_time
|
|
785
|
+
if (Number(req.end_time) < Number(req.start_time)) {
|
|
786
|
+
throw new Error(`第${index + 1}个请求:end_time 必须大于等于 start_time`);
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
// Validate repeated_id is present
|
|
791
|
+
if (!req.repeated_id) {
|
|
792
|
+
throw new Error(`第${index + 1}个请求:repeated_id 必填`);
|
|
793
|
+
}
|
|
794
|
+
});
|
|
795
|
+
|
|
727
796
|
const json = await this.postWecomDocApi({
|
|
728
797
|
path: "/cgi-bin/wedoc/get_form_statistic",
|
|
729
798
|
actionLabel: "get_form_statistic",
|
|
@@ -758,30 +827,90 @@ export class WecomDocClient {
|
|
|
758
827
|
}
|
|
759
828
|
|
|
760
829
|
async updateDocContent(params: { agent: ResolvedAgentAccount; docId: string; requests: UpdateRequest[]; version?: number; batchMode?: boolean }) {
|
|
761
|
-
const { agent, docId, requests, version
|
|
830
|
+
const { agent, docId, requests, version } = params;
|
|
762
831
|
|
|
763
832
|
// Validate requests structure basic check
|
|
764
833
|
const requestList = readArray(requests);
|
|
765
834
|
if (requestList.length === 0) {
|
|
766
835
|
throw new Error("requests list cannot be empty");
|
|
767
836
|
}
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
docid: readString(docId),
|
|
771
|
-
requests: requestList,
|
|
772
|
-
};
|
|
773
|
-
// version is optional but recommended for concurrency control
|
|
837
|
+
|
|
838
|
+
// Validate version difference (≤100 per official API)
|
|
774
839
|
if (version !== undefined && version !== null) {
|
|
775
|
-
|
|
840
|
+
const currentContent = await this.getDocContent({ agent, docId });
|
|
841
|
+
const versionDiff = Math.abs(currentContent.version - version);
|
|
842
|
+
if (versionDiff > 100) {
|
|
843
|
+
throw new Error(`version 与最新版本差值不能超过 100(当前版本:${currentContent.version},传入版本:${version},差值:${versionDiff})`);
|
|
844
|
+
}
|
|
776
845
|
}
|
|
777
846
|
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
847
|
+
// Validate each request's ranges count (≤10 per official API)
|
|
848
|
+
requestList.forEach((req: any, index: number) => {
|
|
849
|
+
if (req.replace_text?.ranges && req.replace_text.ranges.length > 10) {
|
|
850
|
+
throw new Error(`第${index + 1}个操作:replace_text.ranges 不能超过 10 个`);
|
|
851
|
+
}
|
|
852
|
+
if (req.update_text_property?.ranges && req.update_text_property.ranges.length > 10) {
|
|
853
|
+
throw new Error(`第${index + 1}个操作:update_text_property.ranges 不能超过 10 个`);
|
|
854
|
+
}
|
|
855
|
+
// Validate insert_table limits
|
|
856
|
+
if (req.insert_table) {
|
|
857
|
+
const { rows, cols } = req.insert_table;
|
|
858
|
+
if (rows > 100) throw new Error(`第${index + 1}个操作:insert_table 行数不能超过 100`);
|
|
859
|
+
if (cols > 60) throw new Error(`第${index + 1}个操作:insert_table 列数不能超过 60`);
|
|
860
|
+
if (rows * cols > 1000) throw new Error(`第${index + 1}个操作:insert_table 单元格总数不能超过 1000`);
|
|
861
|
+
}
|
|
862
|
+
});
|
|
863
|
+
|
|
864
|
+
// Official API limit: ≤30 operations per batch
|
|
865
|
+
const MAX_OPERATIONS = 30;
|
|
866
|
+
if (requestList.length <= MAX_OPERATIONS) {
|
|
867
|
+
// Single batch
|
|
868
|
+
const body: Record<string, unknown> = {
|
|
869
|
+
docid: readString(docId),
|
|
870
|
+
requests: requestList,
|
|
871
|
+
};
|
|
872
|
+
if (version !== undefined && version !== null) {
|
|
873
|
+
body.version = Number(version);
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
const json = await this.postWecomDocApi({
|
|
877
|
+
path: "/cgi-bin/wedoc/document/batch_update",
|
|
878
|
+
actionLabel: "update_doc_content",
|
|
879
|
+
agent,
|
|
880
|
+
body,
|
|
881
|
+
}) as BatchUpdateDocResponse;
|
|
882
|
+
return { raw: json, batches: 1 };
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
// Auto-batch: split into multiple requests
|
|
886
|
+
// Note: Each batch updates the version, so we need to get latest version for each batch
|
|
887
|
+
const batches: BatchUpdateDocResponse[] = [];
|
|
888
|
+
for (let i = 0; i < requestList.length; i += MAX_OPERATIONS) {
|
|
889
|
+
const batchRequests = requestList.slice(i, i + MAX_OPERATIONS);
|
|
890
|
+
|
|
891
|
+
// Get latest version before each batch (except first if version provided)
|
|
892
|
+
let currentVersion = version;
|
|
893
|
+
if (i > 0 || currentVersion === undefined || currentVersion === null) {
|
|
894
|
+
const content = await this.getDocContent({ agent, docId });
|
|
895
|
+
currentVersion = content.version;
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
const body: Record<string, unknown> = {
|
|
899
|
+
docid: readString(docId),
|
|
900
|
+
requests: batchRequests,
|
|
901
|
+
version: currentVersion,
|
|
902
|
+
};
|
|
903
|
+
|
|
904
|
+
const json = await this.postWecomDocApi({
|
|
905
|
+
path: "/cgi-bin/wedoc/document/batch_update",
|
|
906
|
+
actionLabel: `update_doc_content_batch_${Math.floor(i / MAX_OPERATIONS) + 1}`,
|
|
907
|
+
agent,
|
|
908
|
+
body,
|
|
909
|
+
}) as BatchUpdateDocResponse;
|
|
910
|
+
batches.push(json);
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
return { raw: batches[batches.length - 1], batches: batches.length, allBatches: batches };
|
|
785
914
|
}
|
|
786
915
|
|
|
787
916
|
// --- Spreadsheet Operations ---
|
|
@@ -824,8 +953,9 @@ export class WecomDocClient {
|
|
|
824
953
|
startRow?: number;
|
|
825
954
|
startColumn?: number;
|
|
826
955
|
gridData?: any;
|
|
956
|
+
requests?: any[]; // For direct batch_update with multiple operations
|
|
827
957
|
}) {
|
|
828
|
-
const { agent, docId, sheetId, startRow = 0, startColumn = 0, gridData } = params;
|
|
958
|
+
const { agent, docId, sheetId, startRow = 0, startColumn = 0, gridData, requests } = params;
|
|
829
959
|
|
|
830
960
|
// Validate required docId
|
|
831
961
|
const normalizedDocId = readString(docId);
|
|
@@ -839,15 +969,72 @@ export class WecomDocClient {
|
|
|
839
969
|
throw new Error('sheetId is required');
|
|
840
970
|
}
|
|
841
971
|
|
|
972
|
+
// Handle direct requests (for multiple operations)
|
|
973
|
+
if (requests && requests.length > 0) {
|
|
974
|
+
// Official API limit: ≤5 operations per batch
|
|
975
|
+
const MAX_OPERATIONS = 5;
|
|
976
|
+
|
|
977
|
+
// Validate each request
|
|
978
|
+
requests.forEach((req: any, index: number) => {
|
|
979
|
+
if (req.update_range_request?.grid_data?.rows) {
|
|
980
|
+
const rows = req.update_range_request.grid_data.rows;
|
|
981
|
+
const rowCount = rows.length;
|
|
982
|
+
const rowWidths = rows.map((row: any) => row.values?.length || 0);
|
|
983
|
+
const columnCount = rowWidths.length > 0 ? Math.max(...rowWidths) : 0;
|
|
984
|
+
const totalCells = rowWidths.reduce((sum: number, width: number) => sum + width, 0);
|
|
985
|
+
|
|
986
|
+
if (rowCount > 1000) throw new Error(`第${index + 1}个操作:行数不能超过 1000`);
|
|
987
|
+
if (columnCount > 200) throw new Error(`第${index + 1}个操作:列数不能超过 200`);
|
|
988
|
+
if (totalCells > 10000) throw new Error(`第${index + 1}个操作:单元格总数不能超过 10000`);
|
|
989
|
+
}
|
|
990
|
+
});
|
|
991
|
+
|
|
992
|
+
if (requests.length > MAX_OPERATIONS) {
|
|
993
|
+
throw new Error(`单次批量更新最多${MAX_OPERATIONS}个操作,当前:${requests.length}`);
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
const body = {
|
|
997
|
+
docid: normalizedDocId,
|
|
998
|
+
requests: requests
|
|
999
|
+
};
|
|
1000
|
+
|
|
1001
|
+
const json = await this.postWecomDocApi({
|
|
1002
|
+
path: "/cgi-bin/wedoc/spreadsheet/batch_update",
|
|
1003
|
+
actionLabel: "spreadsheet_batch_update",
|
|
1004
|
+
agent, body,
|
|
1005
|
+
});
|
|
1006
|
+
return {
|
|
1007
|
+
raw: json,
|
|
1008
|
+
docId: normalizedDocId,
|
|
1009
|
+
operations: requests.length
|
|
1010
|
+
};
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
// Handle single gridData update
|
|
1014
|
+
if (!gridData) {
|
|
1015
|
+
throw new Error('gridData or requests is required');
|
|
1016
|
+
}
|
|
1017
|
+
|
|
842
1018
|
// Build GridData per official API
|
|
843
1019
|
// gridData.rows[i].values[j] must be: {cell_value: {text} | {link: {text, url}}, cell_format?: {...}}
|
|
844
|
-
const rows = (gridData
|
|
1020
|
+
const rows = (gridData.rows || []).map((row: any) => ({
|
|
845
1021
|
values: (row.values || []).map((cell: any) => {
|
|
846
1022
|
// If already CellData format, use as-is
|
|
847
1023
|
if (cell && typeof cell === 'object' && cell.cell_value) {
|
|
848
1024
|
return cell;
|
|
849
1025
|
}
|
|
850
|
-
//
|
|
1026
|
+
// Support link simplified format: { url: '...', text: '...' }
|
|
1027
|
+
if (cell && typeof cell === 'object' && cell.url) {
|
|
1028
|
+
return {
|
|
1029
|
+
cell_value: {
|
|
1030
|
+
link: {
|
|
1031
|
+
url: String(cell.url),
|
|
1032
|
+
text: String(cell.text ?? cell.url)
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
};
|
|
1036
|
+
}
|
|
1037
|
+
// Otherwise wrap primitive as CellValue with text
|
|
851
1038
|
return { cell_value: { text: String(cell ?? '') } };
|
|
852
1039
|
})
|
|
853
1040
|
}));
|
|
@@ -874,8 +1061,7 @@ export class WecomDocClient {
|
|
|
874
1061
|
rows: rows
|
|
875
1062
|
};
|
|
876
1063
|
|
|
877
|
-
// Build batch_update request per official API
|
|
878
|
-
// Note: requests array length ≤ 5 per API spec
|
|
1064
|
+
// Build batch_update request per official API (single operation)
|
|
879
1065
|
const body = {
|
|
880
1066
|
docid: normalizedDocId,
|
|
881
1067
|
requests: [{
|
|
@@ -893,7 +1079,7 @@ export class WecomDocClient {
|
|
|
893
1079
|
});
|
|
894
1080
|
return {
|
|
895
1081
|
raw: json,
|
|
896
|
-
docId:
|
|
1082
|
+
docId: normalizedDocId,
|
|
897
1083
|
updatedCells: json.data?.responses?.[0]?.update_range_response?.updated_cells || 0
|
|
898
1084
|
};
|
|
899
1085
|
}
|
|
@@ -978,13 +1164,18 @@ export class WecomDocClient {
|
|
|
978
1164
|
return { raw: json, docId };
|
|
979
1165
|
}
|
|
980
1166
|
|
|
981
|
-
async smartTableGetSheets(params: { agent: ResolvedAgentAccount; docId: string }) {
|
|
982
|
-
const { agent, docId } = params;
|
|
1167
|
+
async smartTableGetSheets(params: { agent: ResolvedAgentAccount; docId: string; sheet_id?: string; need_all_type_sheet?: boolean }) {
|
|
1168
|
+
const { agent, docId, sheet_id, need_all_type_sheet } = params;
|
|
1169
|
+
const payload: Record<string, unknown> = {
|
|
1170
|
+
docid: readString(docId),
|
|
1171
|
+
};
|
|
1172
|
+
if (sheet_id) payload.sheet_id = sheet_id;
|
|
1173
|
+
if (need_all_type_sheet !== undefined) payload.need_all_type_sheet = need_all_type_sheet;
|
|
983
1174
|
const json = await this.postWecomDocApi({
|
|
984
1175
|
path: "/cgi-bin/wedoc/smartsheet/get_sheet",
|
|
985
1176
|
actionLabel: "smartsheet_get_sheet",
|
|
986
1177
|
agent,
|
|
987
|
-
body:
|
|
1178
|
+
body: payload,
|
|
988
1179
|
});
|
|
989
1180
|
return {
|
|
990
1181
|
raw: json,
|
|
@@ -1006,34 +1197,278 @@ export class WecomDocClient {
|
|
|
1006
1197
|
const { agent, docId, sheetId, title } = params;
|
|
1007
1198
|
return this.smartTableOperate({ agent, docId, operation: "update_sheet", bodyData: { properties: { sheet_id: sheetId, title } } });
|
|
1008
1199
|
}
|
|
1009
|
-
async smartTableAddView(params: {
|
|
1010
|
-
|
|
1011
|
-
|
|
1200
|
+
async smartTableAddView(params: {
|
|
1201
|
+
agent: ResolvedAgentAccount;
|
|
1202
|
+
docId: string;
|
|
1203
|
+
sheetId: string;
|
|
1204
|
+
view_title: string;
|
|
1205
|
+
view_type: string;
|
|
1206
|
+
property?: any; // ViewProperty: sort_spec, filter_spec, group_spec, etc.
|
|
1207
|
+
property_gantt?: any; // Deprecated, use property instead
|
|
1208
|
+
property_calendar?: any; // Deprecated, use property instead
|
|
1209
|
+
}) {
|
|
1210
|
+
const { agent, docId, sheetId, view_title, view_type, property, property_gantt, property_calendar } = params;
|
|
1211
|
+
const payload: Record<string, unknown> = {
|
|
1212
|
+
docid: readString(docId),
|
|
1213
|
+
sheet_id: readString(sheetId),
|
|
1214
|
+
view_title: readString(view_title),
|
|
1215
|
+
view_type: readString(view_type),
|
|
1216
|
+
};
|
|
1217
|
+
if (property && typeof property === 'object') {
|
|
1218
|
+
payload.property = property;
|
|
1219
|
+
}
|
|
1220
|
+
// Support deprecated property_gantt/property_calendar for backward compatibility
|
|
1221
|
+
if (property_gantt) payload.property_gantt = property_gantt;
|
|
1222
|
+
if (property_calendar) payload.property_calendar = property_calendar;
|
|
1223
|
+
|
|
1224
|
+
const json = await this.postWecomDocApi({
|
|
1225
|
+
path: "/cgi-bin/wedoc/smartsheet/add_view",
|
|
1226
|
+
actionLabel: "smartsheet_add_view",
|
|
1227
|
+
agent,
|
|
1228
|
+
body: payload,
|
|
1229
|
+
});
|
|
1230
|
+
return {
|
|
1231
|
+
raw: json,
|
|
1232
|
+
view: json.view,
|
|
1233
|
+
};
|
|
1012
1234
|
}
|
|
1013
1235
|
|
|
1014
|
-
async smartTableUpdateView(params: {
|
|
1015
|
-
|
|
1016
|
-
|
|
1236
|
+
async smartTableUpdateView(params: {
|
|
1237
|
+
agent: ResolvedAgentAccount;
|
|
1238
|
+
docId: string;
|
|
1239
|
+
sheetId: string;
|
|
1240
|
+
view_id: string;
|
|
1241
|
+
view_title?: string;
|
|
1242
|
+
property?: any; // ViewProperty: sort_spec, filter_spec, group_spec, etc.
|
|
1243
|
+
property_gantt?: any; // Deprecated, use property instead
|
|
1244
|
+
property_calendar?: any; // Deprecated, use property instead
|
|
1245
|
+
}) {
|
|
1246
|
+
const { agent, docId, sheetId, view_id, view_title, property, property_gantt, property_calendar } = params;
|
|
1247
|
+
const payload: Record<string, unknown> = {
|
|
1248
|
+
docid: readString(docId),
|
|
1249
|
+
sheet_id: readString(sheetId),
|
|
1250
|
+
view_id: readString(view_id),
|
|
1251
|
+
};
|
|
1252
|
+
if (view_title) payload.view_title = readString(view_title);
|
|
1253
|
+
if (property && typeof property === 'object') {
|
|
1254
|
+
payload.property = property;
|
|
1255
|
+
}
|
|
1256
|
+
// Support deprecated property_gantt/property_calendar for backward compatibility
|
|
1257
|
+
if (property_gantt) payload.property_gantt = property_gantt;
|
|
1258
|
+
if (property_calendar) payload.property_calendar = property_calendar;
|
|
1259
|
+
|
|
1260
|
+
const json = await this.postWecomDocApi({
|
|
1261
|
+
path: "/cgi-bin/wedoc/smartsheet/update_view",
|
|
1262
|
+
actionLabel: "smartsheet_update_view",
|
|
1263
|
+
agent,
|
|
1264
|
+
body: payload,
|
|
1265
|
+
});
|
|
1266
|
+
return {
|
|
1267
|
+
raw: json,
|
|
1268
|
+
view: json.view,
|
|
1269
|
+
};
|
|
1017
1270
|
}
|
|
1018
1271
|
|
|
1019
1272
|
async smartTableDelView(params: { agent: ResolvedAgentAccount; docId: string; sheetId: string; view_ids: string[] }) {
|
|
1020
1273
|
const { agent, docId, sheetId, view_ids } = params;
|
|
1021
|
-
|
|
1274
|
+
|
|
1275
|
+
if (!Array.isArray(view_ids) || view_ids.length === 0) {
|
|
1276
|
+
throw new Error("view_ids 必须是非空数组");
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
return this.postWecomDocApi({
|
|
1280
|
+
path: "/cgi-bin/wedoc/smartsheet/delete_views",
|
|
1281
|
+
actionLabel: "smartsheet_del_view",
|
|
1282
|
+
agent,
|
|
1283
|
+
body: {
|
|
1284
|
+
docid: readString(docId),
|
|
1285
|
+
sheet_id: readString(sheetId),
|
|
1286
|
+
view_ids: view_ids,
|
|
1287
|
+
},
|
|
1288
|
+
});
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
async smartTableGetViews(params: {
|
|
1292
|
+
agent: ResolvedAgentAccount;
|
|
1293
|
+
docId: string;
|
|
1294
|
+
sheetId: string;
|
|
1295
|
+
view_ids?: string[];
|
|
1296
|
+
offset?: number;
|
|
1297
|
+
limit?: number;
|
|
1298
|
+
}) {
|
|
1299
|
+
const { agent, docId, sheetId, view_ids, offset, limit } = params;
|
|
1300
|
+
const payload: Record<string, unknown> = {
|
|
1301
|
+
docid: readString(docId),
|
|
1302
|
+
sheet_id: readString(sheetId),
|
|
1303
|
+
};
|
|
1304
|
+
if (view_ids && Array.isArray(view_ids)) payload.view_ids = view_ids;
|
|
1305
|
+
if (offset !== undefined) payload.offset = offset;
|
|
1306
|
+
if (limit !== undefined) payload.limit = limit;
|
|
1307
|
+
|
|
1308
|
+
const json = await this.postWecomDocApi({
|
|
1309
|
+
path: "/cgi-bin/wedoc/smartsheet/get_views",
|
|
1310
|
+
actionLabel: "smartsheet_get_views",
|
|
1311
|
+
agent,
|
|
1312
|
+
body: payload,
|
|
1313
|
+
});
|
|
1314
|
+
return {
|
|
1315
|
+
raw: json,
|
|
1316
|
+
views: readArray(json.views),
|
|
1317
|
+
total: json.total,
|
|
1318
|
+
has_more: json.has_more,
|
|
1319
|
+
next: json.next,
|
|
1320
|
+
};
|
|
1022
1321
|
}
|
|
1023
1322
|
|
|
1024
|
-
async smartTableAddFields(params: {
|
|
1323
|
+
async smartTableAddFields(params: {
|
|
1324
|
+
agent: ResolvedAgentAccount;
|
|
1325
|
+
docId: string;
|
|
1326
|
+
sheetId: string;
|
|
1327
|
+
fields: any[];
|
|
1328
|
+
}) {
|
|
1025
1329
|
const { agent, docId, sheetId, fields } = params;
|
|
1026
|
-
|
|
1330
|
+
|
|
1331
|
+
// Validate fields per official API spec
|
|
1332
|
+
if (!Array.isArray(fields) || fields.length === 0) {
|
|
1333
|
+
throw new Error("fields 必须是非空数组");
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
// Validate each field has required field_title and field_type
|
|
1337
|
+
fields.forEach((field: any, index: number) => {
|
|
1338
|
+
if (!field.field_title) {
|
|
1339
|
+
throw new Error(`第${index + 1}个字段:field_title 必填`);
|
|
1340
|
+
}
|
|
1341
|
+
if (!field.field_type) {
|
|
1342
|
+
throw new Error(`第${index + 1}个字段:field_type 必填`);
|
|
1343
|
+
}
|
|
1344
|
+
// Validate field_type is valid enum value
|
|
1345
|
+
const validFieldTypes = [
|
|
1346
|
+
'FIELD_TYPE_TEXT', 'FIELD_TYPE_NUMBER', 'FIELD_TYPE_CHECKBOX',
|
|
1347
|
+
'FIELD_TYPE_DATE_TIME', 'FIELD_TYPE_IMAGE', 'FIELD_TYPE_ATTACHMENT',
|
|
1348
|
+
'FIELD_TYPE_USER', 'FIELD_TYPE_URL', 'FIELD_TYPE_SELECT',
|
|
1349
|
+
'FIELD_TYPE_CREATED_USER', 'FIELD_TYPE_MODIFIED_USER', 'FIELD_TYPE_CREATED_TIME',
|
|
1350
|
+
'FIELD_TYPE_MODIFIED_TIME', 'FIELD_TYPE_PROGRESS', 'FIELD_TYPE_PHONE_NUMBER',
|
|
1351
|
+
'FIELD_TYPE_EMAIL', 'FIELD_TYPE_SINGLE_SELECT', 'FIELD_TYPE_REFERENCE',
|
|
1352
|
+
'FIELD_TYPE_LOCATION', 'FIELD_TYPE_CURRENCY', 'FIELD_TYPE_WWGROUP',
|
|
1353
|
+
'FIELD_TYPE_AUTONUMBER', 'FIELD_TYPE_PERCENTAGE', 'FIELD_TYPE_BARCODE'
|
|
1354
|
+
];
|
|
1355
|
+
if (!validFieldTypes.includes(field.field_type)) {
|
|
1356
|
+
throw new Error(`第${index + 1}个字段:field_type 必须是有效的字段类型(见 FieldType 枚举)`);
|
|
1357
|
+
}
|
|
1358
|
+
});
|
|
1359
|
+
|
|
1360
|
+
const json = await this.postWecomDocApi({
|
|
1361
|
+
path: "/cgi-bin/wedoc/smartsheet/add_fields",
|
|
1362
|
+
actionLabel: "smartsheet_add_fields",
|
|
1363
|
+
agent,
|
|
1364
|
+
body: {
|
|
1365
|
+
docid: readString(docId),
|
|
1366
|
+
sheet_id: readString(sheetId),
|
|
1367
|
+
fields: fields,
|
|
1368
|
+
},
|
|
1369
|
+
});
|
|
1370
|
+
return {
|
|
1371
|
+
raw: json,
|
|
1372
|
+
fields: readArray(json.fields),
|
|
1373
|
+
};
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
async smartTableUpdateFields(params: {
|
|
1377
|
+
agent: ResolvedAgentAccount;
|
|
1378
|
+
docId: string;
|
|
1379
|
+
sheetId: string;
|
|
1380
|
+
fields: any[];
|
|
1381
|
+
}) {
|
|
1382
|
+
const { agent, docId, sheetId, fields } = params;
|
|
1383
|
+
|
|
1384
|
+
// Validate fields per official API spec
|
|
1385
|
+
if (!Array.isArray(fields) || fields.length === 0) {
|
|
1386
|
+
throw new Error("fields 必须是非空数组");
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
// Validate each field has required field_id and field_type
|
|
1390
|
+
fields.forEach((field: any, index: number) => {
|
|
1391
|
+
if (!field.field_id) {
|
|
1392
|
+
throw new Error(`第${index + 1}个字段:field_id 必填`);
|
|
1393
|
+
}
|
|
1394
|
+
if (!field.field_type) {
|
|
1395
|
+
throw new Error(`第${index + 1}个字段:field_type 必填`);
|
|
1396
|
+
}
|
|
1397
|
+
// field_title is optional for update, but at least one of field_title or property_* must be provided
|
|
1398
|
+
if (!field.field_title && !Object.keys(field).some(key => key.startsWith('property_'))) {
|
|
1399
|
+
throw new Error(`第${index + 1}个字段:field_title 或 property_* 属性至少提供一个`);
|
|
1400
|
+
}
|
|
1401
|
+
});
|
|
1402
|
+
|
|
1403
|
+
const json = await this.postWecomDocApi({
|
|
1404
|
+
path: "/cgi-bin/wedoc/smartsheet/update_fields",
|
|
1405
|
+
actionLabel: "smartsheet_update_fields",
|
|
1406
|
+
agent,
|
|
1407
|
+
body: {
|
|
1408
|
+
docid: readString(docId),
|
|
1409
|
+
sheet_id: readString(sheetId),
|
|
1410
|
+
fields: fields,
|
|
1411
|
+
},
|
|
1412
|
+
});
|
|
1413
|
+
return {
|
|
1414
|
+
raw: json,
|
|
1415
|
+
fields: readArray(json.fields),
|
|
1416
|
+
};
|
|
1027
1417
|
}
|
|
1028
1418
|
|
|
1029
1419
|
async smartTableDelFields(params: { agent: ResolvedAgentAccount; docId: string; sheetId: string; field_ids: string[] }) {
|
|
1030
1420
|
const { agent, docId, sheetId, field_ids } = params;
|
|
1031
|
-
|
|
1421
|
+
|
|
1422
|
+
if (!Array.isArray(field_ids) || field_ids.length === 0) {
|
|
1423
|
+
throw new Error("field_ids 必须是非空数组");
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
return this.postWecomDocApi({
|
|
1427
|
+
path: "/cgi-bin/wedoc/smartsheet/delete_fields",
|
|
1428
|
+
actionLabel: "smartsheet_del_fields",
|
|
1429
|
+
agent,
|
|
1430
|
+
body: {
|
|
1431
|
+
docid: readString(docId),
|
|
1432
|
+
sheet_id: readString(sheetId),
|
|
1433
|
+
field_ids: field_ids,
|
|
1434
|
+
},
|
|
1435
|
+
});
|
|
1032
1436
|
}
|
|
1033
1437
|
|
|
1034
|
-
async
|
|
1035
|
-
|
|
1036
|
-
|
|
1438
|
+
async smartTableGetFields(params: {
|
|
1439
|
+
agent: ResolvedAgentAccount;
|
|
1440
|
+
docId: string;
|
|
1441
|
+
sheetId: string;
|
|
1442
|
+
view_id?: string;
|
|
1443
|
+
field_ids?: string[];
|
|
1444
|
+
field_titles?: string[];
|
|
1445
|
+
offset?: number;
|
|
1446
|
+
limit?: number;
|
|
1447
|
+
}) {
|
|
1448
|
+
const { agent, docId, sheetId, view_id, field_ids, field_titles, offset, limit } = params;
|
|
1449
|
+
const payload: Record<string, unknown> = {
|
|
1450
|
+
docid: readString(docId),
|
|
1451
|
+
sheet_id: readString(sheetId),
|
|
1452
|
+
};
|
|
1453
|
+
if (view_id) payload.view_id = view_id;
|
|
1454
|
+
if (field_ids && Array.isArray(field_ids)) payload.field_ids = field_ids;
|
|
1455
|
+
if (field_titles && Array.isArray(field_titles)) payload.field_titles = field_titles;
|
|
1456
|
+
if (offset !== undefined) payload.offset = offset;
|
|
1457
|
+
if (limit !== undefined) payload.limit = limit;
|
|
1458
|
+
|
|
1459
|
+
const json = await this.postWecomDocApi({
|
|
1460
|
+
path: "/cgi-bin/wedoc/smartsheet/get_fields",
|
|
1461
|
+
actionLabel: "smartsheet_get_fields",
|
|
1462
|
+
agent,
|
|
1463
|
+
body: payload,
|
|
1464
|
+
});
|
|
1465
|
+
return {
|
|
1466
|
+
raw: json,
|
|
1467
|
+
fields: readArray(json.fields),
|
|
1468
|
+
total: json.total,
|
|
1469
|
+
has_more: json.has_more,
|
|
1470
|
+
next: json.next,
|
|
1471
|
+
};
|
|
1037
1472
|
}
|
|
1038
1473
|
|
|
1039
1474
|
async smartTableAddGroup(params: { agent: ResolvedAgentAccount; docId: string; sheetId: string; name: string; children?: string[] }) {
|
|
@@ -1066,9 +1501,37 @@ export class WecomDocClient {
|
|
|
1066
1501
|
return this.smartTableOperate({ agent, docId, operation: "update_external_records", bodyData: { sheet_id: sheetId, records } });
|
|
1067
1502
|
}
|
|
1068
1503
|
|
|
1069
|
-
async smartTableAddRecords(params: {
|
|
1070
|
-
|
|
1071
|
-
|
|
1504
|
+
async smartTableAddRecords(params: {
|
|
1505
|
+
agent: ResolvedAgentAccount;
|
|
1506
|
+
docId: string;
|
|
1507
|
+
sheetId: string;
|
|
1508
|
+
records: any[];
|
|
1509
|
+
key_type?: string;
|
|
1510
|
+
}) {
|
|
1511
|
+
const { agent, docId, sheetId, records, key_type } = params;
|
|
1512
|
+
|
|
1513
|
+
// Validate records format per official API spec (doc2.txt line 1594-1601)
|
|
1514
|
+
if (!Array.isArray(records) || records.length === 0) {
|
|
1515
|
+
throw new Error("records 必须是非空数组");
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1518
|
+
// Validate each record has values object
|
|
1519
|
+
records.forEach((record: any, index: number) => {
|
|
1520
|
+
if (!record.values || typeof record.values !== 'object') {
|
|
1521
|
+
throw new Error(`第${index + 1}条记录缺少 values 对象`);
|
|
1522
|
+
}
|
|
1523
|
+
});
|
|
1524
|
+
|
|
1525
|
+
const bodyData: Record<string, unknown> = {
|
|
1526
|
+
sheet_id: readString(sheetId),
|
|
1527
|
+
records: records,
|
|
1528
|
+
};
|
|
1529
|
+
|
|
1530
|
+
if (key_type) {
|
|
1531
|
+
bodyData.key_type = key_type;
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
return this.smartTableOperate({ agent, docId, operation: "add_records", bodyData });
|
|
1072
1535
|
}
|
|
1073
1536
|
|
|
1074
1537
|
async smartTableUpdateRecords(params: { agent: ResolvedAgentAccount; docId: string; sheetId: string; records: any[] }) {
|
|
@@ -1081,9 +1544,51 @@ export class WecomDocClient {
|
|
|
1081
1544
|
return this.smartTableOperate({ agent, docId, operation: "delete_records", bodyData: { sheet_id: sheetId, record_ids } });
|
|
1082
1545
|
}
|
|
1083
1546
|
|
|
1084
|
-
async smartTableGetRecords(params: {
|
|
1085
|
-
|
|
1086
|
-
|
|
1547
|
+
async smartTableGetRecords(params: {
|
|
1548
|
+
agent: ResolvedAgentAccount;
|
|
1549
|
+
docId: string;
|
|
1550
|
+
sheetId: string;
|
|
1551
|
+
view_id?: string;
|
|
1552
|
+
record_ids?: string[];
|
|
1553
|
+
key_type?: string;
|
|
1554
|
+
field_titles?: string[];
|
|
1555
|
+
field_ids?: string[];
|
|
1556
|
+
sort?: any[];
|
|
1557
|
+
offset?: number;
|
|
1558
|
+
limit?: number;
|
|
1559
|
+
ver?: number;
|
|
1560
|
+
filter_spec?: any;
|
|
1561
|
+
}) {
|
|
1562
|
+
const { agent, docId, sheetId, view_id, record_ids, key_type, field_titles, field_ids, sort, offset, limit, ver, filter_spec } = params;
|
|
1563
|
+
const payload: Record<string, unknown> = {
|
|
1564
|
+
docid: readString(docId),
|
|
1565
|
+
sheet_id: readString(sheetId),
|
|
1566
|
+
};
|
|
1567
|
+
if (view_id) payload.view_id = view_id;
|
|
1568
|
+
if (record_ids && Array.isArray(record_ids)) payload.record_ids = record_ids;
|
|
1569
|
+
if (key_type) payload.key_type = key_type;
|
|
1570
|
+
if (field_titles && Array.isArray(field_titles)) payload.field_titles = field_titles;
|
|
1571
|
+
if (field_ids && Array.isArray(field_ids)) payload.field_ids = field_ids;
|
|
1572
|
+
if (sort && Array.isArray(sort)) payload.sort = sort;
|
|
1573
|
+
if (offset !== undefined) payload.offset = offset;
|
|
1574
|
+
if (limit !== undefined) payload.limit = limit;
|
|
1575
|
+
if (ver !== undefined) payload.ver = ver;
|
|
1576
|
+
if (filter_spec && typeof filter_spec === 'object') payload.filter_spec = filter_spec;
|
|
1577
|
+
|
|
1578
|
+
const json = await this.postWecomDocApi({
|
|
1579
|
+
path: "/cgi-bin/wedoc/smartsheet/get_records",
|
|
1580
|
+
actionLabel: "smartsheet_get_records",
|
|
1581
|
+
agent,
|
|
1582
|
+
body: payload,
|
|
1583
|
+
});
|
|
1584
|
+
return {
|
|
1585
|
+
raw: json,
|
|
1586
|
+
records: readArray(json.records),
|
|
1587
|
+
total: json.total,
|
|
1588
|
+
has_more: json.has_more,
|
|
1589
|
+
next: json.next,
|
|
1590
|
+
ver: json.ver,
|
|
1591
|
+
};
|
|
1087
1592
|
}
|
|
1088
1593
|
|
|
1089
1594
|
// --- Smartsheet Content Permissions ---
|
|
@@ -1173,13 +1678,13 @@ export class WecomDocClient {
|
|
|
1173
1678
|
});
|
|
1174
1679
|
}
|
|
1175
1680
|
|
|
1176
|
-
async getDocAdvancedAccountList(params: { agent: ResolvedAgentAccount;
|
|
1177
|
-
const { agent,
|
|
1681
|
+
async getDocAdvancedAccountList(params: { agent: ResolvedAgentAccount; cursor?: number; limit?: number }) {
|
|
1682
|
+
const { agent, cursor, limit } = params;
|
|
1178
1683
|
return this.postWecomDocApi({
|
|
1179
1684
|
path: "/cgi-bin/meeting/vip/get_vip_user_list",
|
|
1180
1685
|
actionLabel: "get_advanced_account_list",
|
|
1181
1686
|
agent,
|
|
1182
|
-
body: { cursor:
|
|
1687
|
+
body: { cursor: cursor !== undefined ? String(cursor) : undefined, limit: limit ?? 100 },
|
|
1183
1688
|
});
|
|
1184
1689
|
}
|
|
1185
1690
|
|