UNPKG

feishu-mcp

Version:

Model Context Protocol server for Feishu integration

129 lines (128 loc) 6.01 kB
import { Logger } from '../../../utils/logger.js'; import { TaskCreateBatchSchema, TaskDeleteBatchSchema, } from '../../../types/taskSchema.js'; function toNested(item) { const members = []; if (item.assigneeIds?.length) { members.push(...item.assigneeIds.map((id) => ({ id, type: 'user', role: 'assignee' }))); } if (item.followerIds?.length) { members.push(...item.followerIds.map((id) => ({ id, type: 'user', role: 'follower' }))); } return { summary: item.summary, ...(item.parentTaskGuid !== undefined && { parentTaskGuid: item.parentTaskGuid }), ...(item.description !== undefined && { description: item.description }), ...(item.dueTimestamp !== undefined && { due: { timestamp: item.dueTimestamp, is_all_day: item.isDueAllDay } }), ...(item.completedAt !== undefined && { completed_at: item.completedAt }), ...(members.length > 0 && { members }), ...(item.repeatRule !== undefined && { repeat_rule: item.repeatRule }), ...(item.startTimestamp !== undefined && { start: { timestamp: item.startTimestamp, is_all_day: item.isStartAllDay }, }), ...(item.mode !== undefined && { mode: item.mode }), ...(item.isMilestone !== undefined && { is_milestone: item.isMilestone }), ...(item.subTasks?.length && { subTasks: item.subTasks.map(toNested) }), }; } /** * 批量创建飞书任务(含嵌套子任务) */ export async function createTasks(taskItems, api) { const parsed = TaskCreateBatchSchema.safeParse(taskItems); if (!parsed.success) { throw new Error(`参数校验失败: ${parsed.error.message}`); } Logger.info(`createTasks invoked: ${parsed.data.length} top-level items`); const rootItems = parsed.data.map(toNested); const result = await api.createTasksNested(rootItems); return result; } /** * 列取飞书任务(我负责的) */ export async function listTasks(options, api) { Logger.info(`listTasks invoked: pageToken=${options?.pageToken ? 'yes' : 'no'}, completed=${options?.completed ?? 'all'}`); return api.listTasks(options?.pageToken, options?.completed); } /** * 更新飞书任务 */ export async function updateTask(params, api) { const { taskGuid, ...rest } = params; const hasFieldUpdate = rest.summary !== undefined || rest.description !== undefined || rest.dueTimestamp !== undefined || rest.completedAt !== undefined || rest.repeatRule !== undefined || rest.startTimestamp !== undefined || rest.mode !== undefined || rest.isMilestone !== undefined; const hasAdd = (rest.addAssigneeIds?.length ?? 0) + (rest.addFollowerIds?.length ?? 0) > 0; const hasRemove = (rest.removeAssigneeIds?.length ?? 0) + (rest.removeFollowerIds?.length ?? 0) > 0; const hasAddReminder = rest.addReminderRelativeMinutes !== undefined; const hasRemoveReminder = (rest.removeReminderIds?.length ?? 0) > 0; if (!hasFieldUpdate && !hasAdd && !hasRemove && !hasAddReminder && !hasRemoveReminder) { throw new Error('至少需要一项更新:summary/description/due/completed_at/repeat_rule/start/mode/is_milestone、成员 add/remove、或提醒 addReminderRelativeMinutes/removeReminderIds'); } Logger.info(`updateTask invoked: taskGuid=${taskGuid}`); let result; if (hasFieldUpdate) { const updateParams = {}; if (rest.summary !== undefined) updateParams.summary = rest.summary; if (rest.description !== undefined) updateParams.description = rest.description; if (rest.dueTimestamp !== undefined) updateParams.due = { timestamp: rest.dueTimestamp, is_all_day: rest.isDueAllDay }; if (rest.completedAt !== undefined) updateParams.completed_at = rest.completedAt; if (rest.repeatRule !== undefined) updateParams.repeat_rule = rest.repeatRule; if (rest.startTimestamp !== undefined) updateParams.start = { timestamp: rest.startTimestamp, is_all_day: rest.isStartAllDay }; if (rest.mode !== undefined) updateParams.mode = rest.mode; if (rest.isMilestone !== undefined) updateParams.is_milestone = rest.isMilestone; result = await api.updateTask(taskGuid, updateParams); } if (hasAdd) { const members = []; if (rest.addAssigneeIds?.length) { members.push(...rest.addAssigneeIds.map((id) => ({ id, type: 'user', role: 'assignee' }))); } if (rest.addFollowerIds?.length) { members.push(...rest.addFollowerIds.map((id) => ({ id, type: 'user', role: 'follower' }))); } result = await api.addTaskMembers(taskGuid, members); } if (hasRemove) { const members = []; if (rest.removeAssigneeIds?.length) { members.push(...rest.removeAssigneeIds.map((id) => ({ id, type: 'user', role: 'assignee' }))); } if (rest.removeFollowerIds?.length) { members.push(...rest.removeFollowerIds.map((id) => ({ id, type: 'user', role: 'follower' }))); } result = await api.removeTaskMembers(taskGuid, members); } if (hasRemoveReminder) { result = await api.removeTaskReminders(taskGuid, rest.removeReminderIds); } if (hasAddReminder) { result = await api.addTaskReminder(taskGuid, rest.addReminderRelativeMinutes); } const taskEntity = result != null && typeof result === 'object' && 'task' in result && result.task != null ? result.task : result; return taskEntity; } /** * 批量删除飞书任务 */ export async function deleteTasks(taskGuids, api) { const parsed = TaskDeleteBatchSchema.safeParse(taskGuids); if (!parsed.success) { throw new Error(`参数校验失败: ${parsed.error.message}`); } Logger.info(`deleteTasks invoked: ${parsed.data.length} tasks`); return api.deleteTasks(parsed.data); }