@yanhaidao/wecom 2.3.150 → 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 +238 -385
- package/SKILLS_CAL.md +895 -0
- package/SKILLS_DOC.md +2136 -0
- package/changelog/v2.3.16.md +11 -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/target.ts +7 -4
- package/src/transport/bot-ws/inbound.test.ts +46 -0
- package/src/transport/bot-ws/inbound.ts +23 -5
- 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
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { formatErrorMessage, type OpenClawConfig, type PluginRuntime } from "openclaw/plugin-sdk";
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import type { ResolvedRuntimeAccount } from "../config/runtime-config.js";
|
|
3
|
+
import { WecomAuditLog } from "../observability/audit-log.js";
|
|
4
|
+
import { WecomStatusRegistry } from "../observability/status-registry.js";
|
|
5
5
|
import { summarizeTransportSessions } from "../observability/transport-session-view.js";
|
|
6
|
+
import { dispatchInboundEvent } from "../runtime/dispatcher.js";
|
|
7
|
+
import { WecomMediaService } from "../shared/media-service.js";
|
|
8
|
+
import { InMemoryRuntimeStore } from "../store/memory-store.js";
|
|
6
9
|
import type {
|
|
7
10
|
AccountRuntimeStatusSnapshot,
|
|
8
11
|
ReplyHandle,
|
|
@@ -14,10 +17,6 @@ import type {
|
|
|
14
17
|
WecomRuntimeHealth,
|
|
15
18
|
WecomTransportKind,
|
|
16
19
|
} from "../types/index.js";
|
|
17
|
-
import type { ResolvedRuntimeAccount } from "../config/runtime-config.js";
|
|
18
|
-
import { dispatchInboundEvent } from "../runtime/dispatcher.js";
|
|
19
|
-
import { WecomAuditLog } from "../observability/audit-log.js";
|
|
20
|
-
import { WecomStatusRegistry } from "../observability/status-registry.js";
|
|
21
20
|
|
|
22
21
|
export class WecomAccountRuntime {
|
|
23
22
|
readonly store = new InMemoryRuntimeStore();
|
|
@@ -60,23 +59,37 @@ export class WecomAccountRuntime {
|
|
|
60
59
|
}
|
|
61
60
|
|
|
62
61
|
async handleEvent(event: UnifiedInboundEvent, replyHandle: ReplyHandle): Promise<void> {
|
|
62
|
+
const dispatchStartedAt = Date.now();
|
|
63
63
|
this.runtimeStatus.lastInboundAt = Date.now();
|
|
64
64
|
this.runtimeStatus.recentInboundSummary = `${event.transport} ${event.inboundKind} ${event.messageId}`;
|
|
65
65
|
this.log.info?.(
|
|
66
66
|
`[wecom-runtime] inbound account=${event.accountId} transport=${event.transport} kind=${event.inboundKind} messageId=${event.messageId} peer=${event.conversation.peerKind}:${event.conversation.peerId}`,
|
|
67
67
|
);
|
|
68
|
+
this.log.info?.(
|
|
69
|
+
`[wecom-runtime] dispatch-start account=${event.accountId} transport=${event.transport} kind=${event.inboundKind} messageId=${event.messageId}`,
|
|
70
|
+
);
|
|
68
71
|
this.emitStatus();
|
|
69
72
|
|
|
70
73
|
const trackedReplyHandle: ReplyHandle = {
|
|
71
74
|
context: replyHandle.context,
|
|
72
75
|
deliver: async (payload: ReplyPayload, info) => {
|
|
76
|
+
const deliverStartedAt = Date.now();
|
|
77
|
+
const textLen = payload.text?.trim().length ?? 0;
|
|
78
|
+
const mediaCount = (payload.mediaUrls?.length ?? 0) + (payload.mediaUrl ? 1 : 0);
|
|
79
|
+
this.log.info?.(
|
|
80
|
+
`[wecom-runtime] deliver-start account=${event.accountId} transport=${replyHandle.context.transport} kind=${info.kind} messageId=${event.messageId} textLen=${textLen} mediaCount=${mediaCount} reasoning=${String(payload.isReasoning === true)}`,
|
|
81
|
+
);
|
|
73
82
|
await replyHandle.deliver(payload, info);
|
|
74
83
|
this.runtimeStatus.lastOutboundAt = Date.now();
|
|
75
|
-
const outboundSummary =
|
|
84
|
+
const outboundSummary =
|
|
85
|
+
payload.text?.trim() || payload.mediaUrl || payload.mediaUrls?.[0] || info.kind;
|
|
76
86
|
this.runtimeStatus.recentOutboundSummary = `${replyHandle.context.transport} ${outboundSummary.slice(0, 120)}`;
|
|
77
87
|
this.log.info?.(
|
|
78
88
|
`[wecom-runtime] outbound account=${event.accountId} transport=${replyHandle.context.transport} kind=${info.kind} messageId=${event.messageId} summary=${JSON.stringify(this.runtimeStatus.recentOutboundSummary)}`,
|
|
79
89
|
);
|
|
90
|
+
this.log.info?.(
|
|
91
|
+
`[wecom-runtime] deliver-done account=${event.accountId} transport=${replyHandle.context.transport} kind=${info.kind} messageId=${event.messageId} durationMs=${Date.now() - deliverStartedAt}`,
|
|
92
|
+
);
|
|
80
93
|
this.emitStatus();
|
|
81
94
|
},
|
|
82
95
|
fail: async (error: unknown) => {
|
|
@@ -96,15 +109,25 @@ export class WecomAccountRuntime {
|
|
|
96
109
|
},
|
|
97
110
|
};
|
|
98
111
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
112
|
+
try {
|
|
113
|
+
await dispatchInboundEvent({
|
|
114
|
+
core: this.core,
|
|
115
|
+
cfg: this.cfg,
|
|
116
|
+
store: this.store,
|
|
117
|
+
auditLog: this.auditLog,
|
|
118
|
+
mediaService: this.mediaService,
|
|
119
|
+
event,
|
|
120
|
+
replyHandle: trackedReplyHandle,
|
|
121
|
+
});
|
|
122
|
+
this.log.info?.(
|
|
123
|
+
`[wecom-runtime] dispatch-done account=${event.accountId} transport=${event.transport} kind=${event.inboundKind} messageId=${event.messageId} durationMs=${Date.now() - dispatchStartedAt}`,
|
|
124
|
+
);
|
|
125
|
+
} catch (error) {
|
|
126
|
+
this.log.error?.(
|
|
127
|
+
`[wecom-runtime] dispatch-fail account=${event.accountId} transport=${event.transport} kind=${event.inboundKind} messageId=${event.messageId} durationMs=${Date.now() - dispatchStartedAt} error=${formatErrorMessage(error)}`,
|
|
128
|
+
);
|
|
129
|
+
throw error;
|
|
130
|
+
}
|
|
108
131
|
}
|
|
109
132
|
|
|
110
133
|
updateTransportSession(snapshot: TransportSessionSnapshot): void {
|
|
@@ -148,7 +171,7 @@ export class WecomAccountRuntime {
|
|
|
148
171
|
lastDisconnectedAt: patch.lastDisconnectedAt ?? current?.lastDisconnectedAt,
|
|
149
172
|
lastInboundAt: patch.lastInboundAt ?? current?.lastInboundAt,
|
|
150
173
|
lastOutboundAt: patch.lastOutboundAt ?? current?.lastOutboundAt,
|
|
151
|
-
lastError: "lastError" in patch ? patch.lastError ?? undefined : current?.lastError,
|
|
174
|
+
lastError: "lastError" in patch ? (patch.lastError ?? undefined) : current?.lastError,
|
|
152
175
|
};
|
|
153
176
|
this.updateTransportSession(next);
|
|
154
177
|
}
|
|
@@ -173,7 +196,7 @@ export class WecomAccountRuntime {
|
|
|
173
196
|
authenticated: primarySession?.authenticated,
|
|
174
197
|
lastError:
|
|
175
198
|
primarySession?.lastError ??
|
|
176
|
-
(primarySession?.running ? null : this.runtimeStatus.lastError ?? null),
|
|
199
|
+
(primarySession?.running ? null : (this.runtimeStatus.lastError ?? null)),
|
|
177
200
|
transportSessions: summarizeTransportSessions(sessions),
|
|
178
201
|
};
|
|
179
202
|
}
|
|
@@ -202,7 +225,10 @@ export class WecomAccountRuntime {
|
|
|
202
225
|
this.runtimeStatus.lastErrorAt = Date.now();
|
|
203
226
|
this.runtimeStatus.recentIssueCategory = params.category;
|
|
204
227
|
this.runtimeStatus.recentIssueSummary = params.summary;
|
|
205
|
-
const sink =
|
|
228
|
+
const sink =
|
|
229
|
+
params.category === "runtime-error" || params.category === "fallback-delivery-failed"
|
|
230
|
+
? this.log.error
|
|
231
|
+
: this.log.warn;
|
|
206
232
|
sink?.(
|
|
207
233
|
`[wecom-runtime] issue account=${this.account.accountId} transport=${params.transport} category=${params.category} messageId=${params.messageId ?? "n/a"} summary=${params.summary}`,
|
|
208
234
|
);
|
package/src/app/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { PluginRuntime } from "openclaw/plugin-sdk";
|
|
2
|
-
|
|
2
|
+
import { clearWecomSourceAccount } from "../runtime/source-registry.js";
|
|
3
3
|
import { WecomAccountRuntime } from "./account-runtime.js";
|
|
4
4
|
|
|
5
5
|
let runtime: PluginRuntime | null = null;
|
|
@@ -9,6 +9,23 @@ const botWsPushHandles = new Map<string, BotWsPushHandle>();
|
|
|
9
9
|
export type BotWsPushHandle = {
|
|
10
10
|
isConnected: () => boolean;
|
|
11
11
|
sendMarkdown: (chatId: string, content: string) => Promise<void>;
|
|
12
|
+
replyCommand: (params: {
|
|
13
|
+
cmd: string;
|
|
14
|
+
body?: Record<string, unknown>;
|
|
15
|
+
headers?: Record<string, string>;
|
|
16
|
+
}) => Promise<Record<string, unknown>>;
|
|
17
|
+
sendMedia: (params: {
|
|
18
|
+
chatId: string;
|
|
19
|
+
mediaUrl: string;
|
|
20
|
+
text?: string;
|
|
21
|
+
mediaLocalRoots?: readonly string[];
|
|
22
|
+
}) => Promise<{
|
|
23
|
+
ok: boolean;
|
|
24
|
+
messageId?: string;
|
|
25
|
+
rejected?: boolean;
|
|
26
|
+
rejectReason?: string;
|
|
27
|
+
error?: string;
|
|
28
|
+
}>;
|
|
12
29
|
};
|
|
13
30
|
|
|
14
31
|
export function setWecomRuntime(next: PluginRuntime): void {
|
|
@@ -50,5 +67,6 @@ export function unregisterBotWsPushHandle(accountId: string): void {
|
|
|
50
67
|
export function unregisterAccountRuntime(accountId: string): void {
|
|
51
68
|
runtimes.delete(accountId);
|
|
52
69
|
botWsPushHandles.delete(accountId);
|
|
70
|
+
clearWecomSourceAccount(accountId);
|
|
53
71
|
console.log(`[wecom-runtime] unregister account=${accountId}`);
|
|
54
72
|
}
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
# WeCom Calendar Skills 技能清单
|
|
2
|
+
|
|
3
|
+
## 技能列表
|
|
4
|
+
|
|
5
|
+
### wecom_calendar - 企业微信日历工具
|
|
6
|
+
|
|
7
|
+
**技能 ID**: `wecom_calendar`
|
|
8
|
+
**标签**: `WeCom Calendar`
|
|
9
|
+
**描述**: 企业微信日历工具。支持创建/更新/删除日历和日程,管理日程参与者,获取日历详情等功能。
|
|
10
|
+
|
|
11
|
+
## 支持的 Actions (13 个)
|
|
12
|
+
|
|
13
|
+
### 日历管理 (4 个)
|
|
14
|
+
|
|
15
|
+
#### 1. calendar_create - 创建日历
|
|
16
|
+
**参数**:
|
|
17
|
+
- `summary` (必填): 日历标题,1-128 字符
|
|
18
|
+
- `color` (必填): RGB 颜色编码,如 #FF3030
|
|
19
|
+
- `description` (可选): 日历描述,0-512 字符
|
|
20
|
+
- `admins` (可选): 日历管理员 userid 列表,最多 3 人
|
|
21
|
+
- `set_as_default` (可选): 是否设为默认日历,0-否,1-是(第三方应用不支持)
|
|
22
|
+
- `shares` (可选): 通知范围成员列表,最多 2000 人
|
|
23
|
+
- `is_public` (可选): 是否公共日历,0-否,1-是
|
|
24
|
+
- `public_range` (可选): 公开范围
|
|
25
|
+
- `userids`: 公开的成员列表,最多 1000 个
|
|
26
|
+
- `partyids`: 公开的部门列表,最多 100 个
|
|
27
|
+
- `is_corp_calendar` (可选): 是否全员日历,0-否,1-是
|
|
28
|
+
|
|
29
|
+
**返回**: `{ ok: true, action: "calendar_create", calId: string, raw: object }`
|
|
30
|
+
|
|
31
|
+
#### 2. calendar_update - 更新日历
|
|
32
|
+
**参数**:
|
|
33
|
+
- `cal_id` (必填): 日历 ID
|
|
34
|
+
- `summary` (必填): 日历标题,1-128 字符
|
|
35
|
+
- `color` (必填): RGB 颜色编码
|
|
36
|
+
- `description` (可选): 日历描述,0-512 字符
|
|
37
|
+
- `admins` (可选): 日历管理员,最多 3 人
|
|
38
|
+
- `shares` (可选): 通知范围成员,最多 2000 人
|
|
39
|
+
- `public_range` (可选): 公开范围
|
|
40
|
+
- `skip_public_range` (可选): 是否不更新可订阅范围,0-否,1-是
|
|
41
|
+
|
|
42
|
+
**返回**: `{ ok: true, action: "calendar_update", calId: string, raw: object }`
|
|
43
|
+
|
|
44
|
+
#### 3. calendar_get - 获取日历详情
|
|
45
|
+
**参数**:
|
|
46
|
+
- `cal_id_list` (必填): 日历 ID 列表,一次最多 1000 条
|
|
47
|
+
|
|
48
|
+
**返回**: `{ ok: true, action: "calendar_get", calendarList: array, raw: object }`
|
|
49
|
+
|
|
50
|
+
#### 4. calendar_delete - 删除日历
|
|
51
|
+
**参数**:
|
|
52
|
+
- `cal_id` (必填): 日历 ID
|
|
53
|
+
|
|
54
|
+
**返回**: `{ ok: true, action: "calendar_delete", calId: string, raw: object }`
|
|
55
|
+
|
|
56
|
+
### 日程管理 (7 个)
|
|
57
|
+
|
|
58
|
+
#### 5. schedule_create - 创建日程
|
|
59
|
+
**参数**:
|
|
60
|
+
- `start_time` (必填): 日程开始时间,Unix 时间戳(秒)
|
|
61
|
+
- `end_time` (必填): 日程结束时间,Unix 时间戳(秒)
|
|
62
|
+
- `is_whole_day` (可选): 是否全天日程,0-否,1-是
|
|
63
|
+
- `summary` (可选): 日程标题,0-128 字符
|
|
64
|
+
- `description` (可选): 日程描述,不多于 1000 字符
|
|
65
|
+
- `location` (可选): 日程地址,不多于 128 字符
|
|
66
|
+
- `attendees` (可选): 日程参与者列表,最多 1000 人
|
|
67
|
+
- `admins` (可选): 日程管理员,最多 3 人
|
|
68
|
+
- `reminders` (可选): 提醒相关信息
|
|
69
|
+
- `is_remind`: 是否需要提醒,0-否,1-是
|
|
70
|
+
- `is_repeat`: 是否重复日程,0-否,1-是
|
|
71
|
+
- `remind_before_event_secs`: 日程开始前多少秒提醒 [0,300,900,3600,86400]
|
|
72
|
+
- `remind_time_diffs`: 提醒时间与日程开始时间的差值数组
|
|
73
|
+
- `repeat_type`: 重复类型 [0-每日,1-每周,2-每月,5-每年,7-工作日]
|
|
74
|
+
- `repeat_until`: 重复结束时刻,Unix 时间戳
|
|
75
|
+
- `is_custom_repeat`: 是否自定义重复,0-否,1-是
|
|
76
|
+
- `repeat_interval`: 重复间隔
|
|
77
|
+
- `repeat_day_of_week`: 每周周几重复 [1-7]
|
|
78
|
+
- `repeat_day_of_month`: 每月哪几天重复 [1-31]
|
|
79
|
+
- `timezone`: 时区 [-12 到 +12]
|
|
80
|
+
- `cal_id` (可选): 日程所属日历 ID(第三方应用必须指定)
|
|
81
|
+
|
|
82
|
+
**返回**: `{ ok: true, action: "schedule_create", scheduleId: string, raw: object }`
|
|
83
|
+
|
|
84
|
+
#### 6. schedule_update - 更新日程
|
|
85
|
+
**参数**:
|
|
86
|
+
- `schedule_id` (必填): 日程 ID
|
|
87
|
+
- `start_time` (必填): 日程开始时间
|
|
88
|
+
- `end_time` (必填): 日程结束时间
|
|
89
|
+
- `is_whole_day` (可选): 是否全天日程
|
|
90
|
+
- `summary` (可选): 日程标题
|
|
91
|
+
- `description` (可选): 日程描述
|
|
92
|
+
- `location` (可选): 日程地址
|
|
93
|
+
- `attendees` (可选): 日程参与者
|
|
94
|
+
- `admins` (可选): 日程管理员
|
|
95
|
+
- `reminders` (可选): 提醒相关信息
|
|
96
|
+
- `skip_attendees` (可选): 是否不更新参与人,0-否,1-是
|
|
97
|
+
- `op_mode` (可选): 操作模式 [0-全部修改,1-仅修改此日程,2-修改将来的所有日程]
|
|
98
|
+
- `op_start_time` (可选): 操作起始时间
|
|
99
|
+
|
|
100
|
+
**返回**: `{ ok: true, action: "schedule_update", scheduleId: string, raw: object }`
|
|
101
|
+
|
|
102
|
+
#### 7. schedule_add_attendees - 新增日程参与者
|
|
103
|
+
**参数**:
|
|
104
|
+
- `schedule_id` (必填): 日程 ID
|
|
105
|
+
- `attendees` (必填): 日程参与者列表,累计最多 1000 人
|
|
106
|
+
|
|
107
|
+
**返回**: `{ ok: true, action: "schedule_add_attendees", scheduleId: string, raw: object }`
|
|
108
|
+
|
|
109
|
+
#### 8. schedule_del_attendees - 删除日程参与者
|
|
110
|
+
**参数**:
|
|
111
|
+
- `schedule_id` (必填): 日程 ID
|
|
112
|
+
- `attendees` (必填): 日程参与者列表,最多 1000 人
|
|
113
|
+
|
|
114
|
+
**返回**: `{ ok: true, action: "schedule_del_attendees", scheduleId: string, raw: object }`
|
|
115
|
+
|
|
116
|
+
#### 9. schedule_get_by_calendar - 获取日历下的日程列表
|
|
117
|
+
**参数**:
|
|
118
|
+
- `cal_id` (必填): 日历 ID
|
|
119
|
+
- `offset` (可选): 分页偏移量,默认 0
|
|
120
|
+
- `limit` (可选): 分页大小,默认 500,范围 1-1000
|
|
121
|
+
|
|
122
|
+
**返回**: `{ ok: true, action: "schedule_get_by_calendar", scheduleList: array, raw: object }`
|
|
123
|
+
|
|
124
|
+
#### 10. schedule_get - 获取日程详情
|
|
125
|
+
**参数**:
|
|
126
|
+
- `schedule_id_list` (必填): 日程 ID 列表,一次最多 1000 条
|
|
127
|
+
|
|
128
|
+
**返回**: `{ ok: true, action: "schedule_get", scheduleList: array, meetingCode: string, meetingLink: string, raw: object }`
|
|
129
|
+
|
|
130
|
+
#### 11. schedule_delete - 取消日程
|
|
131
|
+
**参数**:
|
|
132
|
+
- `schedule_id` (必填): 日程 ID
|
|
133
|
+
- `op_mode` (可选): 操作模式 [0-删除所有,1-仅删除此日程,2-删除本次及后续]
|
|
134
|
+
- `op_start_time` (可选): 操作起始时间
|
|
135
|
+
|
|
136
|
+
**返回**: `{ ok: true, action: "schedule_delete", scheduleId: string, raw: object }`
|
|
137
|
+
|
|
138
|
+
### 默认日历管理 (2 个)
|
|
139
|
+
|
|
140
|
+
#### 12. schedule_get_system_calid - 获取默认日历本 ID
|
|
141
|
+
**参数**:
|
|
142
|
+
- `userid` (必填): 指定成员的 userid
|
|
143
|
+
|
|
144
|
+
**返回**: `{ ok: true, action: "schedule_get_system_calid", calId: string, raw: object }`
|
|
145
|
+
|
|
146
|
+
#### 13. schedule_create_in_system - 在默认日历创建日程
|
|
147
|
+
**参数**:
|
|
148
|
+
- `organizer` (必填): 日程创建者 userid
|
|
149
|
+
- `start_time` (必填): 日程开始时间
|
|
150
|
+
- `end_time` (必填): 日程结束时间
|
|
151
|
+
- `is_whole_day` (可选): 是否全天日程
|
|
152
|
+
- `summary` (可选): 日程标题
|
|
153
|
+
- `description` (可选): 日程描述
|
|
154
|
+
- `location` (可选): 日程地址
|
|
155
|
+
- `attendees` (可选): 日程参与者
|
|
156
|
+
- `reminders` (可选): 提醒相关信息
|
|
157
|
+
|
|
158
|
+
**返回**: `{ ok: true, action: "schedule_create_in_system", scheduleId: string, raw: object }`
|
|
159
|
+
|
|
160
|
+
## 与官方文档对比验证
|
|
161
|
+
|
|
162
|
+
### 接口完整性 ✓
|
|
163
|
+
- [x] 创建日历 (calendar/add) - 完整实现
|
|
164
|
+
- [x] 更新日历 (calendar/update) - 完整实现
|
|
165
|
+
- [x] 获取日历 (calendar/get) - 完整实现
|
|
166
|
+
- [x] 删除日历 (calendar/del) - 完整实现
|
|
167
|
+
- [x] 创建日程 (schedule/add) - 完整实现
|
|
168
|
+
- [x] 更新日程 (schedule/update) - 完整实现
|
|
169
|
+
- [x] 新增参与者 (schedule/add_attendees) - 完整实现
|
|
170
|
+
- [x] 删除参与者 (schedule/del_attendees) - 完整实现
|
|
171
|
+
- [x] 获取日程列表 (schedule/get_by_calendar) - 完整实现
|
|
172
|
+
- [x] 获取日程详情 (schedule/get) - 完整实现
|
|
173
|
+
- [x] 取消日程 (schedule/del) - 完整实现
|
|
174
|
+
- [x] 获取默认日历 ID (calendar/get_system_calid) - 完整实现
|
|
175
|
+
- [x] 创建默认日历日程 (schedule/add_schedule_in_system_cal) - 完整实现
|
|
176
|
+
- [x] 更新日程回执 (schedule/respond) - 已实现(client.ts)
|
|
177
|
+
- [x] 同步日程 (schedule/sync) - 已实现(client.ts)
|
|
178
|
+
|
|
179
|
+
### 参数验证 ✓
|
|
180
|
+
- [x] 所有必填参数都有 required 声明
|
|
181
|
+
- [x] 所有参数的类型、格式、取值范围都正确
|
|
182
|
+
- [x] 所有接口限制都有验证(数量、字符)
|
|
183
|
+
- [x] 所有枚举值都正确定义
|
|
184
|
+
|
|
185
|
+
### 返回值处理 ✓
|
|
186
|
+
- [x] 所有接口返回结果结构正确
|
|
187
|
+
- [x] 错误处理完善(HTTP 错误、JSON 错误、业务错误)
|
|
188
|
+
- [x] 重试机制实现(3 次)
|
|
189
|
+
|
|
190
|
+
### 特殊功能 ✓
|
|
191
|
+
- [x] op_mode 操作模式支持(0/1/2)
|
|
192
|
+
- [x] op_start_time 重复日程操作
|
|
193
|
+
- [x] 提醒时间枚举值验证
|
|
194
|
+
- [x] 时区范围验证(-12 到 +12)
|
|
195
|
+
- [x] 重复日程自定义设置
|
|
196
|
+
|
|
197
|
+
## 已知限制和注意事项
|
|
198
|
+
|
|
199
|
+
1. **第三方应用**:不支持 `set_as_default` 参数
|
|
200
|
+
2. **全员日历**:每个企业最多 20 个,不支持指定颜色
|
|
201
|
+
3. **公共日历**:每人最多创建或订阅 100 个
|
|
202
|
+
4. **日程数量**:每个应用每天最多 2 万个
|
|
203
|
+
5. **重复日程修改**:需指定 op_mode 和 op_start_time
|
|
204
|
+
6. **会议室关联**:已预约会议室的日程更新时间受限
|
|
205
|
+
|
|
206
|
+
## 待实现功能
|
|
207
|
+
|
|
208
|
+
1. **回调事件处理** - 需要在 webhook 中添加以下事件处理:
|
|
209
|
+
- delete_calendar
|
|
210
|
+
- modify_calendar
|
|
211
|
+
- modify_schedule
|
|
212
|
+
- delete_schedule
|
|
213
|
+
- respond_schedule
|
|
214
|
+
- add_schedule
|
|
215
|
+
|
|
216
|
+
2. **完整实现** - client.ts 和 tool.ts 需要完善所有方法的具体实现
|
|
217
|
+
|
|
218
|
+
## 使用示例
|
|
219
|
+
|
|
220
|
+
```json
|
|
221
|
+
// 创建日历
|
|
222
|
+
{
|
|
223
|
+
"action": "calendar_create",
|
|
224
|
+
"summary": "团队日历",
|
|
225
|
+
"color": "#FF3030",
|
|
226
|
+
"description": "团队共享日历",
|
|
227
|
+
"admins": ["zhangsan", "lisi"],
|
|
228
|
+
"shares": [{"userid": "wangwu", "permission": 1}]
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// 创建日程
|
|
232
|
+
{
|
|
233
|
+
"action": "schedule_create",
|
|
234
|
+
"start_time": 1679011200,
|
|
235
|
+
"end_time": 1679014800,
|
|
236
|
+
"summary": "项目评审会议",
|
|
237
|
+
"location": "10 楼会议室",
|
|
238
|
+
"attendees": [{"userid": "user1"}, {"userid": "user2"}],
|
|
239
|
+
"reminders": {
|
|
240
|
+
"is_remind": 1,
|
|
241
|
+
"remind_before_event_secs": 3600,
|
|
242
|
+
"timezone": 8
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// 获取日程详情
|
|
247
|
+
{
|
|
248
|
+
"action": "schedule_get",
|
|
249
|
+
"schedule_id_list": ["schedule_id_1", "schedule_id_2"]
|
|
250
|
+
}
|
|
251
|
+
```
|