@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
|
@@ -0,0 +1,815 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// Calendar Client - Complete Implementation
|
|
3
|
+
// 严格遵循企业微信官方 API 文档:https://developer.work.weixin.qq.com/document/path/93329
|
|
4
|
+
// ============================================================================
|
|
5
|
+
import type { ResolvedAgentAccount } from "../../types/index.js";
|
|
6
|
+
import { getAccessToken } from "../../transport/agent-api/core.js";
|
|
7
|
+
import { wecomFetch } from "../../http.js";
|
|
8
|
+
import { resolveWecomEgressProxyUrlFromNetwork } from "../../config/index.js";
|
|
9
|
+
import { LIMITS } from "../../types/constants.js";
|
|
10
|
+
import type {
|
|
11
|
+
CreateCalendarRequest, CreateCalendarResponse, UpdateCalendarRequest, UpdateCalendarResponse,
|
|
12
|
+
GetCalendarRequest, GetCalendarResponse, DeleteCalendarResponse,
|
|
13
|
+
CreateScheduleRequest, CreateScheduleResponse, UpdateScheduleRequest, UpdateScheduleResponse,
|
|
14
|
+
AddScheduleAttendeesRequest, AddScheduleAttendeesResponse,
|
|
15
|
+
DeleteScheduleAttendeesRequest, DeleteScheduleAttendeesResponse,
|
|
16
|
+
GetScheduleByCalendarRequest, GetScheduleByCalendarResponse,
|
|
17
|
+
GetScheduleRequest, GetScheduleResponse, DeleteScheduleRequest, DeleteScheduleResponse,
|
|
18
|
+
GetSystemCalendarIdRequest, GetSystemCalendarIdResponse,
|
|
19
|
+
CreateSystemScheduleRequest, RespondScheduleRequest, RespondScheduleResponse,
|
|
20
|
+
SyncScheduleRequest, SyncScheduleResponse, CalendarInfo, ScheduleInfo,
|
|
21
|
+
ScheduleReminders,
|
|
22
|
+
} from "./types.js";
|
|
23
|
+
|
|
24
|
+
// ============================================================================
|
|
25
|
+
// Constants - 官方 API 限定值
|
|
26
|
+
// ============================================================================
|
|
27
|
+
|
|
28
|
+
const CALENDAR_LIMITS = {
|
|
29
|
+
ADMINS_MAX: 3,
|
|
30
|
+
SHARES_MAX: 2000,
|
|
31
|
+
PUBLIC_USERS_MAX: 1000,
|
|
32
|
+
PUBLIC_PARTIES_MAX: 100,
|
|
33
|
+
SUMMARY_MIN_LENGTH: 1,
|
|
34
|
+
SUMMARY_MAX_LENGTH: 128,
|
|
35
|
+
DESCRIPTION_MAX_LENGTH: 512,
|
|
36
|
+
COLOR_PATTERN: /^#?[0-9A-Fa-f]{6}$/,
|
|
37
|
+
SCHEDULE_ATTENDEES_MAX: 1000,
|
|
38
|
+
SCHEDULE_DESCRIPTION_MAX_LENGTH: 1000,
|
|
39
|
+
SCHEDULE_LOCATION_MAX_LENGTH: 128,
|
|
40
|
+
SCHEDULE_SUMMARY_MAX_LENGTH: 128,
|
|
41
|
+
REMINDER_BEFORE_EVENT_VALUES: [0, 300, 900, 3600, 86400],
|
|
42
|
+
REMINDER_TIME_DIFFS_VALUES: [0, -300, -900, -3600, -86400, 32400, -172800, -604800],
|
|
43
|
+
REPEAT_TYPE_VALUES: [0, 1, 2, 5, 7],
|
|
44
|
+
TIMEZONE_MIN: -12,
|
|
45
|
+
TIMEZONE_MAX: 12,
|
|
46
|
+
CAL_ID_LIST_MAX: 1000,
|
|
47
|
+
SCHEDULE_ID_LIST_MAX: 1000,
|
|
48
|
+
GET_SCHEDULE_LIMIT_MIN: 1,
|
|
49
|
+
GET_SCHEDULE_LIMIT_MAX: 1000,
|
|
50
|
+
OP_MODE_VALUES: [0, 1, 2],
|
|
51
|
+
PERMISSION_VALUES: [1, 3],
|
|
52
|
+
RESPONSE_STATUS_VALUES: [1, 2, 4],
|
|
53
|
+
} as const;
|
|
54
|
+
|
|
55
|
+
// ============================================================================
|
|
56
|
+
// Validation Functions
|
|
57
|
+
// ============================================================================
|
|
58
|
+
|
|
59
|
+
function validateString(value: unknown, fieldName: string, opts?: { min?: number; max?: number; pattern?: RegExp; allowEmpty?: boolean }): string {
|
|
60
|
+
const str = String(value ?? "");
|
|
61
|
+
if (!opts?.allowEmpty && !str.trim()) {
|
|
62
|
+
throw new Error(`${fieldName} 不能为空`);
|
|
63
|
+
}
|
|
64
|
+
if (opts?.min !== undefined && str.length < opts.min) {
|
|
65
|
+
throw new Error(`${fieldName} 长度不能少于 ${opts.min} 字符`);
|
|
66
|
+
}
|
|
67
|
+
if (opts?.max !== undefined && str.length > opts.max) {
|
|
68
|
+
throw new Error(`${fieldName} 长度不能超过 ${opts.max} 字符`);
|
|
69
|
+
}
|
|
70
|
+
if (opts?.pattern && !opts.pattern.test(str)) {
|
|
71
|
+
throw new Error(`${fieldName} 格式不正确`);
|
|
72
|
+
}
|
|
73
|
+
return str;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function validateNumber(value: unknown, fieldName: string, opts?: { min?: number; max?: number; required?: boolean }): number {
|
|
77
|
+
if (value === undefined || value === null) {
|
|
78
|
+
if (opts?.required) throw new Error(`${fieldName} 是必填项`);
|
|
79
|
+
return 0;
|
|
80
|
+
}
|
|
81
|
+
const num = Number(value);
|
|
82
|
+
if (isNaN(num)) throw new Error(`${fieldName} 必须是数字`);
|
|
83
|
+
if (opts?.min !== undefined && num < opts.min) throw new Error(`${fieldName} 不能小于 ${opts.min}`);
|
|
84
|
+
if (opts?.max !== undefined && num > opts.max) throw new Error(`${fieldName} 不能大于 ${opts.max}`);
|
|
85
|
+
return num;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function validateArray<T>(value: unknown, fieldName: string, opts?: { min?: number; max?: number; required?: boolean }): any[] {
|
|
89
|
+
if (!Array.isArray(value)) {
|
|
90
|
+
if (opts?.required) throw new Error(`${fieldName} 必须是数组`);
|
|
91
|
+
return [];
|
|
92
|
+
}
|
|
93
|
+
if (opts?.min !== undefined && value.length < opts.min) {
|
|
94
|
+
throw new Error(`${fieldName} 至少需要 ${opts.min} 项`);
|
|
95
|
+
}
|
|
96
|
+
if (opts?.max !== undefined && value.length > opts.max) {
|
|
97
|
+
throw new Error(`${fieldName} 不能超过 ${opts.max} 项`);
|
|
98
|
+
}
|
|
99
|
+
return value;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function validateCalendarAdmins(admins?: string[]): string[] | undefined {
|
|
103
|
+
if (!admins) return undefined;
|
|
104
|
+
validateArray(admins, "admins", { max: CALENDAR_LIMITS.ADMINS_MAX });
|
|
105
|
+
return admins.map((id, i) => validateString(id, `admins[${i}]`, { allowEmpty: false }));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function validateCalendarShares(shares?: Array<{ userid: string; permission?: 1 | 3 }>): Array<{ userid: string; permission?: 1 | 3 }> | undefined {
|
|
109
|
+
if (!shares) return undefined;
|
|
110
|
+
validateArray(shares, "shares", { max: CALENDAR_LIMITS.SHARES_MAX });
|
|
111
|
+
return shares.map((s, i) => {
|
|
112
|
+
const userid = validateString(s.userid, `shares[${i}].userid`, { allowEmpty: false });
|
|
113
|
+
if (s.permission !== undefined && !CALENDAR_LIMITS.PERMISSION_VALUES.includes(s.permission)) {
|
|
114
|
+
throw new Error(`shares[${i}].permission 必须是 1 或 3`);
|
|
115
|
+
}
|
|
116
|
+
return { userid, permission: s.permission };
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function validatePublicRange(publicRange?: { userids?: string[]; partyids?: number[] }): { userids?: string[]; partyids?: number[] } | undefined {
|
|
121
|
+
if (!publicRange) return undefined;
|
|
122
|
+
const result: { userids?: string[]; partyids?: number[] } = {};
|
|
123
|
+
if (publicRange.userids) {
|
|
124
|
+
validateArray(publicRange.userids, "public_range.userids", { max: CALENDAR_LIMITS.PUBLIC_USERS_MAX });
|
|
125
|
+
result.userids = publicRange.userids.map((id, i) => validateString(id, `public_range.userids[${i}]`, { allowEmpty: false }));
|
|
126
|
+
}
|
|
127
|
+
if (publicRange.partyids) {
|
|
128
|
+
validateArray(publicRange.partyids, "public_range.partyids", { max: CALENDAR_LIMITS.PUBLIC_PARTIES_MAX });
|
|
129
|
+
result.partyids = publicRange.partyids.map((id, i) => validateNumber(id, `public_range.partyids[${i}]`, { min: 1, required: true }));
|
|
130
|
+
}
|
|
131
|
+
return result;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function validateScheduleAttendees(attendees?: Array<{ userid: string }>): Array<{ userid: string }> | undefined {
|
|
135
|
+
if (!attendees) return undefined;
|
|
136
|
+
validateArray(attendees, "attendees", { max: CALENDAR_LIMITS.SCHEDULE_ATTENDEES_MAX });
|
|
137
|
+
return attendees.map((a, i) => ({
|
|
138
|
+
userid: validateString(a.userid, `attendees[${i}].userid`, { allowEmpty: false, max: 64 })
|
|
139
|
+
}));
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function validateReminders(reminders?: any): any | undefined {
|
|
143
|
+
if (!reminders) return undefined;
|
|
144
|
+
const result: any = {};
|
|
145
|
+
|
|
146
|
+
if (reminders.is_remind !== undefined) {
|
|
147
|
+
if (![0, 1].includes(reminders.is_remind)) throw new Error("reminders.is_remind 必须是 0 或 1");
|
|
148
|
+
result.is_remind = reminders.is_remind;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (reminders.is_repeat !== undefined) {
|
|
152
|
+
if (![0, 1].includes(reminders.is_repeat)) throw new Error("reminders.is_repeat 必须是 0 或 1");
|
|
153
|
+
result.is_repeat = reminders.is_repeat;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (reminders.remind_before_event_secs !== undefined) {
|
|
157
|
+
const val = reminders.remind_before_event_secs;
|
|
158
|
+
if (!CALENDAR_LIMITS.REMINDER_BEFORE_EVENT_VALUES.includes(val)) {
|
|
159
|
+
throw new Error(`reminders.remind_before_event_secs 必须是 ${CALENDAR_LIMITS.REMINDER_BEFORE_EVENT_VALUES.join(",")}`);
|
|
160
|
+
}
|
|
161
|
+
result.remind_before_event_secs = val;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (reminders.remind_time_diffs !== undefined) {
|
|
165
|
+
validateArray(reminders.remind_time_diffs, "reminders.remind_time_diffs");
|
|
166
|
+
result.remind_time_diffs = reminders.remind_time_diffs.map((v: number, i: number) => {
|
|
167
|
+
if (!CALENDAR_LIMITS.REMINDER_TIME_DIFFS_VALUES.includes(v as any)) {
|
|
168
|
+
throw new Error(`reminders.remind_time_diffs[${i}] 必须是 ${CALENDAR_LIMITS.REMINDER_TIME_DIFFS_VALUES.join(",")}`);
|
|
169
|
+
}
|
|
170
|
+
return v as any;
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (reminders.repeat_type !== undefined) {
|
|
175
|
+
if (!CALENDAR_LIMITS.REPEAT_TYPE_VALUES.includes(reminders.repeat_type)) {
|
|
176
|
+
throw new Error(`reminders.repeat_type 必须是 ${CALENDAR_LIMITS.REPEAT_TYPE_VALUES.join(",")}`);
|
|
177
|
+
}
|
|
178
|
+
result.repeat_type = reminders.repeat_type;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (reminders.repeat_until !== undefined) {
|
|
182
|
+
result.repeat_until = validateNumber(reminders.repeat_until, "reminders.repeat_until", { min: 0 });
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (reminders.is_custom_repeat !== undefined) {
|
|
186
|
+
if (![0, 1].includes(reminders.is_custom_repeat)) {
|
|
187
|
+
throw new Error("reminders.is_custom_repeat 必须是 0 或 1");
|
|
188
|
+
}
|
|
189
|
+
result.is_custom_repeat = reminders.is_custom_repeat;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (reminders.repeat_interval !== undefined) {
|
|
193
|
+
result.repeat_interval = validateNumber(reminders.repeat_interval, "reminders.repeat_interval", { min: 1 });
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (reminders.repeat_day_of_week !== undefined) {
|
|
197
|
+
validateArray(reminders.repeat_day_of_week, "reminders.repeat_day_of_week");
|
|
198
|
+
result.repeat_day_of_week = reminders.repeat_day_of_week.map((v: number, i: number) => {
|
|
199
|
+
if (v < 1 || v > 7) throw new Error(`reminders.repeat_day_of_week[${i}] 必须是 1-7`);
|
|
200
|
+
return v;
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (reminders.repeat_day_of_month !== undefined) {
|
|
205
|
+
validateArray(reminders.repeat_day_of_month, "reminders.repeat_day_of_month");
|
|
206
|
+
result.repeat_day_of_month = reminders.repeat_day_of_month.map((v: number, i: number) => {
|
|
207
|
+
if (v < 1 || v > 31) throw new Error(`reminders.repeat_day_of_month[${i}] 必须是 1-31`);
|
|
208
|
+
return v;
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (reminders.timezone !== undefined) {
|
|
213
|
+
result.timezone = validateNumber(reminders.timezone, "reminders.timezone", {
|
|
214
|
+
min: CALENDAR_LIMITS.TIMEZONE_MIN,
|
|
215
|
+
max: CALENDAR_LIMITS.TIMEZONE_MAX
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (reminders.exclude_time_list !== undefined) {
|
|
220
|
+
validateArray(reminders.exclude_time_list, "reminders.exclude_time_list");
|
|
221
|
+
result.exclude_time_list = reminders.exclude_time_list.map((item: any, i: number) => ({
|
|
222
|
+
start_time: validateNumber(item.start_time, `reminders.exclude_time_list[${i}].start_time`, { min: 0, required: true })
|
|
223
|
+
}));
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return result;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function validateOpMode(opMode?: number): 0 | 1 | 2 | undefined {
|
|
230
|
+
if (opMode === undefined) return undefined;
|
|
231
|
+
if (!CALENDAR_LIMITS.OP_MODE_VALUES.includes(opMode as any)) {
|
|
232
|
+
throw new Error(`op_mode 必须是 ${CALENDAR_LIMITS.OP_MODE_VALUES.join(",")}`);
|
|
233
|
+
}
|
|
234
|
+
return opMode as 0 | 1 | 2;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// ============================================================================
|
|
238
|
+
// Helper Functions
|
|
239
|
+
// ============================================================================
|
|
240
|
+
|
|
241
|
+
async function parseResponse<T>(res: Response, label: string): Promise<T> {
|
|
242
|
+
let json: any;
|
|
243
|
+
try {
|
|
244
|
+
json = await res.json();
|
|
245
|
+
} catch {
|
|
246
|
+
throw new Error(`${label}: 无效的 JSON 响应`);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (!json || typeof json !== "object") {
|
|
250
|
+
throw new Error(`${label}: 空响应`);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (Array.isArray(json)) {
|
|
254
|
+
const failed = json.find((i: any) => Number(i?.errcode ?? 0) !== 0);
|
|
255
|
+
if (failed) throw new Error(`${label}: ${failed?.errmsg || "failed"} (${failed?.errcode})`);
|
|
256
|
+
return json as T;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const errCode = Number(json.errcode ?? 0);
|
|
260
|
+
if (errCode !== 0) {
|
|
261
|
+
throw new Error(`${label}: ${json.errmsg || "failed"} (${errCode})`);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return json as T;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function normalizeColor(color: string): string {
|
|
268
|
+
const trimmed = color.trim();
|
|
269
|
+
return trimmed.startsWith("#") ? trimmed : `#${trimmed}`;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// ============================================================================
|
|
273
|
+
// WecomCalendarClient Class
|
|
274
|
+
// ============================================================================
|
|
275
|
+
|
|
276
|
+
export class WecomCalendarClient {
|
|
277
|
+
private async post<T>(path: string, label: string, agent: ResolvedAgentAccount, body: any): Promise<T> {
|
|
278
|
+
if (!agent?.corpId || !agent?.corpSecret) {
|
|
279
|
+
throw new Error(`${label}: 账号配置不完整,需要 corpId 和 corpSecret`);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const token = await getAccessToken(agent);
|
|
283
|
+
const url = `https://qyapi.weixin.qq.com${path}?access_token=${encodeURIComponent(token)}`;
|
|
284
|
+
const proxyUrl = resolveWecomEgressProxyUrlFromNetwork(agent.network);
|
|
285
|
+
|
|
286
|
+
let lastError: Error | undefined;
|
|
287
|
+
for (let attempt = 1; attempt <= 3; attempt++) {
|
|
288
|
+
try {
|
|
289
|
+
const res = await wecomFetch(
|
|
290
|
+
url,
|
|
291
|
+
{
|
|
292
|
+
method: "POST",
|
|
293
|
+
headers: { "content-type": "application/json" },
|
|
294
|
+
body: JSON.stringify(body || {}),
|
|
295
|
+
},
|
|
296
|
+
{ proxyUrl, timeoutMs: LIMITS.REQUEST_TIMEOUT_MS }
|
|
297
|
+
);
|
|
298
|
+
return await parseResponse<T>(res, label);
|
|
299
|
+
} catch (e) {
|
|
300
|
+
lastError = e instanceof Error ? e : new Error(String(e));
|
|
301
|
+
if (attempt < 3) {
|
|
302
|
+
await new Promise(r => setTimeout(r, 1000 * attempt));
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
throw lastError || new Error(`${label}: 请求失败`);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// ========================================================================
|
|
310
|
+
// Calendar APIs
|
|
311
|
+
// ========================================================================
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* 创建日历
|
|
315
|
+
* POST /cgi-bin/oa/calendar/add
|
|
316
|
+
*/
|
|
317
|
+
async createCalendar(p: { agent: ResolvedAgentAccount; request: CreateCalendarRequest }): Promise<{ raw: CreateCalendarResponse; calId: string }> {
|
|
318
|
+
const calendar = p.request.calendar;
|
|
319
|
+
|
|
320
|
+
// 验证必填字段
|
|
321
|
+
const summary = validateString(calendar.summary, "calendar.summary", {
|
|
322
|
+
min: CALENDAR_LIMITS.SUMMARY_MIN_LENGTH,
|
|
323
|
+
max: CALENDAR_LIMITS.SUMMARY_MAX_LENGTH
|
|
324
|
+
});
|
|
325
|
+
const color = validateString(calendar.color, "calendar.color", { pattern: CALENDAR_LIMITS.COLOR_PATTERN });
|
|
326
|
+
|
|
327
|
+
// 验证可选字段
|
|
328
|
+
const description = calendar.description !== undefined
|
|
329
|
+
? validateString(calendar.description, "calendar.description", { max: CALENDAR_LIMITS.DESCRIPTION_MAX_LENGTH, allowEmpty: true })
|
|
330
|
+
: undefined;
|
|
331
|
+
|
|
332
|
+
const admins = validateCalendarAdmins(calendar.admins);
|
|
333
|
+
const shares = validateCalendarShares(calendar.shares);
|
|
334
|
+
const publicRange = validatePublicRange(calendar.public_range);
|
|
335
|
+
|
|
336
|
+
if (calendar.set_as_default !== undefined && ![0, 1].includes(calendar.set_as_default)) {
|
|
337
|
+
throw new Error("calendar.set_as_default 必须是 0 或 1");
|
|
338
|
+
}
|
|
339
|
+
if (calendar.is_public !== undefined && ![0, 1].includes(calendar.is_public)) {
|
|
340
|
+
throw new Error("calendar.is_public 必须是 0 或 1");
|
|
341
|
+
}
|
|
342
|
+
if (calendar.is_corp_calendar !== undefined && ![0, 1].includes(calendar.is_corp_calendar)) {
|
|
343
|
+
throw new Error("calendar.is_corp_calendar 必须是 0 或 1");
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// 全员日历必须是公共日历且必须指定 public_range
|
|
347
|
+
if (calendar.is_corp_calendar === 1) {
|
|
348
|
+
if (calendar.is_public !== 1) {
|
|
349
|
+
throw new Error("全员日历必须设置 is_public=1");
|
|
350
|
+
}
|
|
351
|
+
if (!publicRange || (!publicRange.userids && !publicRange.partyids)) {
|
|
352
|
+
throw new Error("全员日历必须指定 public_range");
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const request: CreateCalendarRequest = {
|
|
357
|
+
calendar: {
|
|
358
|
+
summary,
|
|
359
|
+
color: normalizeColor(color),
|
|
360
|
+
description,
|
|
361
|
+
admins,
|
|
362
|
+
set_as_default: calendar.set_as_default,
|
|
363
|
+
shares,
|
|
364
|
+
is_public: calendar.is_public,
|
|
365
|
+
public_range: publicRange,
|
|
366
|
+
is_corp_calendar: calendar.is_corp_calendar,
|
|
367
|
+
},
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
if (p.request.agentid !== undefined) {
|
|
371
|
+
request.agentid = p.request.agentid;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
const json = await this.post<CreateCalendarResponse>("/cgi-bin/oa/calendar/add", "create_calendar", p.agent, request);
|
|
375
|
+
return { raw: json, calId: json.cal_id };
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* 更新日历
|
|
380
|
+
* POST /cgi-bin/oa/calendar/update
|
|
381
|
+
* 注意:更新操作是覆盖式,不是增量式
|
|
382
|
+
*/
|
|
383
|
+
async updateCalendar(p: { agent: ResolvedAgentAccount; request: UpdateCalendarRequest }): Promise<{ raw: UpdateCalendarResponse; calId: string }> {
|
|
384
|
+
const calendar = p.request.calendar;
|
|
385
|
+
|
|
386
|
+
const calId = validateString(calendar.cal_id, "calendar.cal_id", { allowEmpty: false });
|
|
387
|
+
const summary = validateString(calendar.summary, "calendar.summary", {
|
|
388
|
+
min: CALENDAR_LIMITS.SUMMARY_MIN_LENGTH,
|
|
389
|
+
max: CALENDAR_LIMITS.SUMMARY_MAX_LENGTH
|
|
390
|
+
});
|
|
391
|
+
const color = validateString(calendar.color, "calendar.color", { pattern: CALENDAR_LIMITS.COLOR_PATTERN });
|
|
392
|
+
const description = calendar.description !== undefined
|
|
393
|
+
? validateString(calendar.description, "calendar.description", { max: CALENDAR_LIMITS.DESCRIPTION_MAX_LENGTH, allowEmpty: true })
|
|
394
|
+
: undefined;
|
|
395
|
+
|
|
396
|
+
const admins = validateCalendarAdmins(calendar.admins);
|
|
397
|
+
const shares = validateCalendarShares(calendar.shares);
|
|
398
|
+
const publicRange = validatePublicRange(calendar.public_range);
|
|
399
|
+
|
|
400
|
+
if (p.request.skip_public_range !== undefined && ![0, 1].includes(p.request.skip_public_range)) {
|
|
401
|
+
throw new Error("skip_public_range 必须是 0 或 1");
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
const request: UpdateCalendarRequest = {
|
|
405
|
+
calendar: {
|
|
406
|
+
cal_id: calId,
|
|
407
|
+
summary,
|
|
408
|
+
color: normalizeColor(color),
|
|
409
|
+
description,
|
|
410
|
+
admins,
|
|
411
|
+
shares,
|
|
412
|
+
public_range: publicRange,
|
|
413
|
+
},
|
|
414
|
+
skip_public_range: p.request.skip_public_range,
|
|
415
|
+
};
|
|
416
|
+
|
|
417
|
+
const json = await this.post<UpdateCalendarResponse>("/cgi-bin/oa/calendar/update", "update_calendar", p.agent, request);
|
|
418
|
+
return { raw: json, calId: calId };
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* 获取日历详情
|
|
423
|
+
* POST /cgi-bin/oa/calendar/get
|
|
424
|
+
*/
|
|
425
|
+
async getCalendar(p: { agent: ResolvedAgentAccount; request: GetCalendarRequest }): Promise<{ raw: GetCalendarResponse; calendarList: CalendarInfo[] }> {
|
|
426
|
+
const calIdList = validateArray(p.request.cal_id_list, "cal_id_list", {
|
|
427
|
+
min: 1,
|
|
428
|
+
max: CALENDAR_LIMITS.CAL_ID_LIST_MAX,
|
|
429
|
+
required: true
|
|
430
|
+
}).map((id, i) => validateString(id, `cal_id_list[${i}]`, { allowEmpty: false }));
|
|
431
|
+
|
|
432
|
+
const request: GetCalendarRequest = { cal_id_list: calIdList };
|
|
433
|
+
const json = await this.post<GetCalendarResponse>("/cgi-bin/oa/calendar/get", "get_calendar", p.agent, request);
|
|
434
|
+
return { raw: json, calendarList: json.calendar_list || [] };
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* 删除日历
|
|
439
|
+
* POST /cgi-bin/oa/calendar/del
|
|
440
|
+
*/
|
|
441
|
+
async deleteCalendar(p: { agent: ResolvedAgentAccount; calId: string }): Promise<{ raw: DeleteCalendarResponse; calId: string }> {
|
|
442
|
+
const calId = validateString(p.calId, "calId", { allowEmpty: false });
|
|
443
|
+
const json = await this.post<DeleteCalendarResponse>("/cgi-bin/oa/calendar/del", "delete_calendar", p.agent, { cal_id: calId });
|
|
444
|
+
return { raw: json, calId: calId };
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// ========================================================================
|
|
448
|
+
// Schedule APIs
|
|
449
|
+
// ========================================================================
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* 创建日程
|
|
453
|
+
* POST /cgi-bin/oa/schedule/add
|
|
454
|
+
*/
|
|
455
|
+
async createSchedule(p: { agent: ResolvedAgentAccount; request: CreateScheduleRequest }): Promise<{ raw: CreateScheduleResponse; scheduleId: string }> {
|
|
456
|
+
const schedule = p.request.schedule;
|
|
457
|
+
|
|
458
|
+
const startTime = validateNumber(schedule.start_time, "schedule.start_time", { min: 0, required: true });
|
|
459
|
+
const endTime = validateNumber(schedule.end_time, "schedule.end_time", { min: 0, required: true });
|
|
460
|
+
|
|
461
|
+
if (endTime <= startTime) {
|
|
462
|
+
throw new Error("schedule.end_time 必须大于 schedule.start_time");
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
if (schedule.is_whole_day !== undefined && ![0, 1].includes(schedule.is_whole_day)) {
|
|
466
|
+
throw new Error("schedule.is_whole_day 必须是 0 或 1");
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
const summary = schedule.summary !== undefined
|
|
470
|
+
? validateString(schedule.summary, "schedule.summary", { max: CALENDAR_LIMITS.SCHEDULE_SUMMARY_MAX_LENGTH, allowEmpty: true })
|
|
471
|
+
: undefined;
|
|
472
|
+
|
|
473
|
+
const description = schedule.description !== undefined
|
|
474
|
+
? validateString(schedule.description, "schedule.description", { max: CALENDAR_LIMITS.SCHEDULE_DESCRIPTION_MAX_LENGTH, allowEmpty: true })
|
|
475
|
+
: undefined;
|
|
476
|
+
|
|
477
|
+
const location = schedule.location !== undefined
|
|
478
|
+
? validateString(schedule.location, "schedule.location", { max: CALENDAR_LIMITS.SCHEDULE_LOCATION_MAX_LENGTH, allowEmpty: true })
|
|
479
|
+
: undefined;
|
|
480
|
+
|
|
481
|
+
const admins = validateCalendarAdmins(schedule.admins);
|
|
482
|
+
const attendees = validateScheduleAttendees(schedule.attendees);
|
|
483
|
+
const reminders = validateReminders(schedule.reminders);
|
|
484
|
+
|
|
485
|
+
const calId = schedule.cal_id !== undefined
|
|
486
|
+
? validateString(schedule.cal_id, "schedule.cal_id", { max: 64, allowEmpty: true })
|
|
487
|
+
: undefined;
|
|
488
|
+
|
|
489
|
+
const request: CreateScheduleRequest = {
|
|
490
|
+
schedule: {
|
|
491
|
+
start_time: startTime,
|
|
492
|
+
end_time: endTime,
|
|
493
|
+
is_whole_day: schedule.is_whole_day,
|
|
494
|
+
summary,
|
|
495
|
+
description,
|
|
496
|
+
location,
|
|
497
|
+
admins,
|
|
498
|
+
attendees,
|
|
499
|
+
reminders,
|
|
500
|
+
cal_id: calId,
|
|
501
|
+
},
|
|
502
|
+
};
|
|
503
|
+
|
|
504
|
+
if (p.request.agentid !== undefined) {
|
|
505
|
+
request.agentid = p.request.agentid;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
const json = await this.post<CreateScheduleResponse>("/cgi-bin/oa/schedule/add", "create_schedule", p.agent, request);
|
|
509
|
+
return { raw: json, scheduleId: json.schedule_id };
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* 更新日程
|
|
514
|
+
* POST /cgi-bin/oa/schedule/update
|
|
515
|
+
* 注意:更新操作是覆盖式,不是增量式
|
|
516
|
+
*/
|
|
517
|
+
async updateSchedule(p: { agent: ResolvedAgentAccount; request: UpdateScheduleRequest }): Promise<{ raw: UpdateScheduleResponse; scheduleId: string }> {
|
|
518
|
+
const schedule = p.request.schedule;
|
|
519
|
+
|
|
520
|
+
const scheduleId = validateString(schedule.schedule_id, "schedule.schedule_id", { allowEmpty: false });
|
|
521
|
+
const startTime = validateNumber(schedule.start_time, "schedule.start_time", { min: 0, required: true });
|
|
522
|
+
const endTime = validateNumber(schedule.end_time, "schedule.end_time", { min: 0, required: true });
|
|
523
|
+
|
|
524
|
+
if (endTime <= startTime) {
|
|
525
|
+
throw new Error("schedule.end_time 必须大于 schedule.start_time");
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
if (schedule.is_whole_day !== undefined && ![0, 1].includes(schedule.is_whole_day)) {
|
|
529
|
+
throw new Error("schedule.is_whole_day 必须是 0 或 1");
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
const summary = schedule.summary !== undefined
|
|
533
|
+
? validateString(schedule.summary, "schedule.summary", { max: CALENDAR_LIMITS.SCHEDULE_SUMMARY_MAX_LENGTH, allowEmpty: true })
|
|
534
|
+
: undefined;
|
|
535
|
+
|
|
536
|
+
const description = schedule.description !== undefined
|
|
537
|
+
? validateString(schedule.description, "schedule.description", { max: CALENDAR_LIMITS.SCHEDULE_DESCRIPTION_MAX_LENGTH, allowEmpty: true })
|
|
538
|
+
: undefined;
|
|
539
|
+
|
|
540
|
+
const location = schedule.location !== undefined
|
|
541
|
+
? validateString(schedule.location, "schedule.location", { max: CALENDAR_LIMITS.SCHEDULE_LOCATION_MAX_LENGTH, allowEmpty: true })
|
|
542
|
+
: undefined;
|
|
543
|
+
|
|
544
|
+
const admins = validateCalendarAdmins(schedule.admins);
|
|
545
|
+
const attendees = validateScheduleAttendees(schedule.attendees);
|
|
546
|
+
const reminders = validateReminders(schedule.reminders);
|
|
547
|
+
|
|
548
|
+
if (p.request.skip_attendees !== undefined && ![0, 1].includes(p.request.skip_attendees)) {
|
|
549
|
+
throw new Error("skip_attendees 必须是 0 或 1");
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
const opMode = validateOpMode(p.request.op_mode);
|
|
553
|
+
const opStartTime = p.request.op_start_time !== undefined
|
|
554
|
+
? validateNumber(p.request.op_start_time, "op_start_time", { min: 0 })
|
|
555
|
+
: undefined;
|
|
556
|
+
|
|
557
|
+
const request: UpdateScheduleRequest = {
|
|
558
|
+
schedule: {
|
|
559
|
+
schedule_id: scheduleId,
|
|
560
|
+
start_time: startTime,
|
|
561
|
+
end_time: endTime,
|
|
562
|
+
is_whole_day: schedule.is_whole_day,
|
|
563
|
+
summary,
|
|
564
|
+
description,
|
|
565
|
+
location,
|
|
566
|
+
admins,
|
|
567
|
+
attendees,
|
|
568
|
+
reminders,
|
|
569
|
+
},
|
|
570
|
+
skip_attendees: p.request.skip_attendees as any,
|
|
571
|
+
op_mode: opMode as any,
|
|
572
|
+
op_start_time: opStartTime,
|
|
573
|
+
};
|
|
574
|
+
|
|
575
|
+
const json = await this.post<UpdateScheduleResponse>("/cgi-bin/oa/schedule/update", "update_schedule", p.agent, request);
|
|
576
|
+
return { raw: json, scheduleId: json.schedule_id || scheduleId };
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
/**
|
|
580
|
+
* 新增日程参与者
|
|
581
|
+
* POST /cgi-bin/oa/schedule/add_attendees
|
|
582
|
+
* 注意:该接口是增量式
|
|
583
|
+
*/
|
|
584
|
+
async addScheduleAttendees(p: { agent: ResolvedAgentAccount; request: AddScheduleAttendeesRequest }): Promise<{ raw: AddScheduleAttendeesResponse; scheduleId: string }> {
|
|
585
|
+
const scheduleId = validateString(p.request.schedule_id, "schedule_id", { allowEmpty: false });
|
|
586
|
+
const attendees = validateScheduleAttendees(p.request.attendees);
|
|
587
|
+
|
|
588
|
+
if (!attendees || attendees.length === 0) {
|
|
589
|
+
throw new Error("attendees 不能为空");
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
const request: AddScheduleAttendeesRequest = {
|
|
593
|
+
schedule_id: scheduleId,
|
|
594
|
+
attendees,
|
|
595
|
+
};
|
|
596
|
+
|
|
597
|
+
const json = await this.post<AddScheduleAttendeesResponse>("/cgi-bin/oa/schedule/add_attendees", "add_attendees", p.agent, request);
|
|
598
|
+
return { raw: json, scheduleId: scheduleId };
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
/**
|
|
602
|
+
* 删除日程参与者
|
|
603
|
+
* POST /cgi-bin/oa/schedule/del_attendees
|
|
604
|
+
* 注意:该接口是增量式
|
|
605
|
+
*/
|
|
606
|
+
async deleteScheduleAttendees(p: { agent: ResolvedAgentAccount; request: DeleteScheduleAttendeesRequest }): Promise<{ raw: DeleteScheduleAttendeesResponse; scheduleId: string }> {
|
|
607
|
+
const scheduleId = validateString(p.request.schedule_id, "schedule_id", { allowEmpty: false });
|
|
608
|
+
const attendees = validateScheduleAttendees(p.request.attendees);
|
|
609
|
+
|
|
610
|
+
if (!attendees || attendees.length === 0) {
|
|
611
|
+
throw new Error("attendees 不能为空");
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
const request: DeleteScheduleAttendeesRequest = {
|
|
615
|
+
schedule_id: scheduleId,
|
|
616
|
+
attendees,
|
|
617
|
+
};
|
|
618
|
+
|
|
619
|
+
const json = await this.post<DeleteScheduleAttendeesResponse>("/cgi-bin/oa/schedule/del_attendees", "del_attendees", p.agent, request);
|
|
620
|
+
return { raw: json, scheduleId: scheduleId };
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
/**
|
|
624
|
+
* 获取日历下的日程列表
|
|
625
|
+
* POST /cgi-bin/oa/schedule/get_by_calendar
|
|
626
|
+
*/
|
|
627
|
+
async getScheduleByCalendar(p: { agent: ResolvedAgentAccount; request: GetScheduleByCalendarRequest }): Promise<{ raw: GetScheduleByCalendarResponse; scheduleList: ScheduleInfo[] }> {
|
|
628
|
+
const calId = validateString(p.request.cal_id, "cal_id", { allowEmpty: false });
|
|
629
|
+
|
|
630
|
+
if (p.request.offset !== undefined && p.request.offset < 0) {
|
|
631
|
+
throw new Error("offset 不能小于 0");
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
let limit = p.request.limit;
|
|
635
|
+
if (limit !== undefined) {
|
|
636
|
+
if (limit < CALENDAR_LIMITS.GET_SCHEDULE_LIMIT_MIN || limit > CALENDAR_LIMITS.GET_SCHEDULE_LIMIT_MAX) {
|
|
637
|
+
throw new Error(`limit 必须在 ${CALENDAR_LIMITS.GET_SCHEDULE_LIMIT_MIN}-${CALENDAR_LIMITS.GET_SCHEDULE_LIMIT_MAX} 之间`);
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
const request: GetScheduleByCalendarRequest = {
|
|
642
|
+
cal_id: calId,
|
|
643
|
+
offset: p.request.offset,
|
|
644
|
+
limit: limit,
|
|
645
|
+
};
|
|
646
|
+
|
|
647
|
+
const json = await this.post<GetScheduleByCalendarResponse>("/cgi-bin/oa/schedule/get_by_calendar", "get_by_calendar", p.agent, request);
|
|
648
|
+
return { raw: json, scheduleList: json.schedule_list || [] };
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
/**
|
|
652
|
+
* 获取日程详情
|
|
653
|
+
* POST /cgi-bin/oa/schedule/get
|
|
654
|
+
*/
|
|
655
|
+
async getSchedule(p: { agent: ResolvedAgentAccount; request: GetScheduleRequest }): Promise<{ raw: GetScheduleResponse; scheduleList: ScheduleInfo[]; meetingCode?: string; meetingLink?: string }> {
|
|
656
|
+
const scheduleIdList = validateArray(p.request.schedule_id_list, "schedule_id_list", {
|
|
657
|
+
min: 1,
|
|
658
|
+
max: CALENDAR_LIMITS.SCHEDULE_ID_LIST_MAX,
|
|
659
|
+
required: true
|
|
660
|
+
}).map((id, i) => validateString(id, `schedule_id_list[${i}]`, { allowEmpty: false }));
|
|
661
|
+
|
|
662
|
+
const request: GetScheduleRequest = { schedule_id_list: scheduleIdList };
|
|
663
|
+
const json = await this.post<GetScheduleResponse>("/cgi-bin/oa/schedule/get", "get_schedule", p.agent, request);
|
|
664
|
+
return {
|
|
665
|
+
raw: json,
|
|
666
|
+
scheduleList: json.schedule_list || [],
|
|
667
|
+
meetingCode: json.meeting_code,
|
|
668
|
+
meetingLink: json.meeting_link,
|
|
669
|
+
};
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
/**
|
|
673
|
+
* 取消日程
|
|
674
|
+
* POST /cgi-bin/oa/schedule/del
|
|
675
|
+
*/
|
|
676
|
+
async deleteSchedule(p: { agent: ResolvedAgentAccount; request: DeleteScheduleRequest }): Promise<{ raw: DeleteScheduleResponse; scheduleId: string }> {
|
|
677
|
+
const scheduleId = validateString(p.request.schedule_id, "schedule_id", { allowEmpty: false });
|
|
678
|
+
const opMode = validateOpMode(p.request.op_mode);
|
|
679
|
+
const opStartTime = p.request.op_start_time !== undefined
|
|
680
|
+
? validateNumber(p.request.op_start_time, "op_start_time", { min: 0 })
|
|
681
|
+
: undefined;
|
|
682
|
+
|
|
683
|
+
const request: DeleteScheduleRequest = {
|
|
684
|
+
schedule_id: scheduleId,
|
|
685
|
+
op_mode: opMode as any,
|
|
686
|
+
op_start_time: opStartTime,
|
|
687
|
+
};
|
|
688
|
+
|
|
689
|
+
const json = await this.post<DeleteScheduleResponse>("/cgi-bin/oa/schedule/del", "delete_schedule", p.agent, request);
|
|
690
|
+
return { raw: json, scheduleId: scheduleId };
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
// ========================================================================
|
|
694
|
+
// System Calendar APIs
|
|
695
|
+
// ========================================================================
|
|
696
|
+
|
|
697
|
+
/**
|
|
698
|
+
* 获取默认日历本 ID
|
|
699
|
+
* POST /cgi-bin/oa/calendar/get_system_calid
|
|
700
|
+
*/
|
|
701
|
+
async getSystemCalendarId(p: { agent: ResolvedAgentAccount; userid: string }): Promise<{ raw: GetSystemCalendarIdResponse; calId: string }> {
|
|
702
|
+
const userid = validateString(p.userid, "userid", { allowEmpty: false });
|
|
703
|
+
const json = await this.post<GetSystemCalendarIdResponse>("/cgi-bin/oa/calendar/get_system_calid", "get_system_calid", p.agent, { userid });
|
|
704
|
+
return { raw: json, calId: json.cal_id };
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
/**
|
|
708
|
+
* 在默认日历本中创建日程
|
|
709
|
+
* POST /cgi-bin/oa/schedule/add_schedule_in_system_cal
|
|
710
|
+
*/
|
|
711
|
+
async createSystemSchedule(p: { agent: ResolvedAgentAccount; request: CreateSystemScheduleRequest }): Promise<{ raw: CreateScheduleResponse; scheduleId: string }> {
|
|
712
|
+
const schedule = p.request.schedule;
|
|
713
|
+
|
|
714
|
+
const organizer = validateString(schedule.organizer, "schedule.organizer", { allowEmpty: false });
|
|
715
|
+
const startTime = validateNumber(schedule.start_time, "schedule.start_time", { min: 0, required: true });
|
|
716
|
+
const endTime = validateNumber(schedule.end_time, "schedule.end_time", { min: 0, required: true });
|
|
717
|
+
|
|
718
|
+
if (endTime <= startTime) {
|
|
719
|
+
throw new Error("schedule.end_time 必须大于 schedule.start_time");
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
if (schedule.is_whole_day !== undefined && ![0, 1].includes(schedule.is_whole_day)) {
|
|
723
|
+
throw new Error("schedule.is_whole_day 必须是 0 或 1");
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
const summary = schedule.summary !== undefined
|
|
727
|
+
? validateString(schedule.summary, "schedule.summary", { max: CALENDAR_LIMITS.SCHEDULE_SUMMARY_MAX_LENGTH, allowEmpty: true })
|
|
728
|
+
: undefined;
|
|
729
|
+
|
|
730
|
+
const description = schedule.description !== undefined
|
|
731
|
+
? validateString(schedule.description, "schedule.description", { max: CALENDAR_LIMITS.SCHEDULE_DESCRIPTION_MAX_LENGTH, allowEmpty: true })
|
|
732
|
+
: undefined;
|
|
733
|
+
|
|
734
|
+
const location = schedule.location !== undefined
|
|
735
|
+
? validateString(schedule.location, "schedule.location", { max: CALENDAR_LIMITS.SCHEDULE_LOCATION_MAX_LENGTH, allowEmpty: true })
|
|
736
|
+
: undefined;
|
|
737
|
+
|
|
738
|
+
const attendees = validateScheduleAttendees(schedule.attendees);
|
|
739
|
+
const reminders = validateReminders(schedule.reminders);
|
|
740
|
+
|
|
741
|
+
const request: CreateSystemScheduleRequest = {
|
|
742
|
+
schedule: {
|
|
743
|
+
organizer,
|
|
744
|
+
start_time: startTime,
|
|
745
|
+
end_time: endTime,
|
|
746
|
+
is_whole_day: schedule.is_whole_day,
|
|
747
|
+
summary,
|
|
748
|
+
description,
|
|
749
|
+
location,
|
|
750
|
+
attendees,
|
|
751
|
+
reminders,
|
|
752
|
+
},
|
|
753
|
+
};
|
|
754
|
+
|
|
755
|
+
const json = await this.post<CreateScheduleResponse>("/cgi-bin/oa/schedule/add_schedule_in_system_cal", "create_system_schedule", p.agent, request);
|
|
756
|
+
return { raw: json, scheduleId: json.schedule_id };
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
/**
|
|
760
|
+
* 日程回执
|
|
761
|
+
* POST /cgi-bin/oa/schedule/respond
|
|
762
|
+
*/
|
|
763
|
+
async respondSchedule(p: { agent: ResolvedAgentAccount; request: RespondScheduleRequest }): Promise<{ raw: RespondScheduleResponse; scheduleId: string }> {
|
|
764
|
+
const scheduleId = validateString(p.request.schedule_id, "schedule_id", { allowEmpty: false });
|
|
765
|
+
const opMode = validateOpMode(p.request.op_mode);
|
|
766
|
+
const opStartTime = p.request.op_start_time !== undefined
|
|
767
|
+
? validateNumber(p.request.op_start_time, "op_start_time", { min: 0 })
|
|
768
|
+
: undefined;
|
|
769
|
+
|
|
770
|
+
const attendee = validateString(p.request.attendee, "attendee", { allowEmpty: false });
|
|
771
|
+
|
|
772
|
+
if (!CALENDAR_LIMITS.RESPONSE_STATUS_VALUES.includes(p.request.response_status)) {
|
|
773
|
+
throw new Error(`response_status 必须是 ${CALENDAR_LIMITS.RESPONSE_STATUS_VALUES.join(",")}`);
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
const request: RespondScheduleRequest = {
|
|
777
|
+
schedule_id: scheduleId,
|
|
778
|
+
op_mode: opMode as any,
|
|
779
|
+
op_start_time: opStartTime,
|
|
780
|
+
attendee,
|
|
781
|
+
response_status: p.request.response_status as any,
|
|
782
|
+
};
|
|
783
|
+
|
|
784
|
+
const json = await this.post<RespondScheduleResponse>("/cgi-bin/oa/schedule/respond", "respond_schedule", p.agent, request);
|
|
785
|
+
return { raw: json, scheduleId: scheduleId };
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
/**
|
|
789
|
+
* 同步日程
|
|
790
|
+
* POST /cgi-bin/oa/schedule/sync
|
|
791
|
+
*/
|
|
792
|
+
async syncSchedule(p: { agent: ResolvedAgentAccount; request: SyncScheduleRequest }): Promise<{ raw: SyncScheduleResponse; nextCursor: string; scheduleList: ScheduleInfo[] }> {
|
|
793
|
+
const calId = validateString(p.request.cal_id, "cal_id", { allowEmpty: false });
|
|
794
|
+
|
|
795
|
+
let limit = p.request.limit;
|
|
796
|
+
if (limit !== undefined) {
|
|
797
|
+
if (limit < CALENDAR_LIMITS.GET_SCHEDULE_LIMIT_MIN || limit > CALENDAR_LIMITS.GET_SCHEDULE_LIMIT_MAX) {
|
|
798
|
+
throw new Error(`limit 必须在 ${CALENDAR_LIMITS.GET_SCHEDULE_LIMIT_MIN}-${CALENDAR_LIMITS.GET_SCHEDULE_LIMIT_MAX} 之间`);
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
const request: SyncScheduleRequest = {
|
|
803
|
+
cal_id: calId,
|
|
804
|
+
cursor: p.request.cursor,
|
|
805
|
+
limit: limit,
|
|
806
|
+
};
|
|
807
|
+
|
|
808
|
+
const json = await this.post<SyncScheduleResponse>("/cgi-bin/oa/schedule/sync", "sync_schedule", p.agent, request);
|
|
809
|
+
return {
|
|
810
|
+
raw: json,
|
|
811
|
+
nextCursor: json.next_cursor,
|
|
812
|
+
scheduleList: json.schedule_list || [],
|
|
813
|
+
};
|
|
814
|
+
}
|
|
815
|
+
}
|