forked from bai/curriculum-project-hub
65 lines
2.2 KiB
TypeScript
65 lines
2.2 KiB
TypeScript
import { randomUUID } from "node:crypto";
|
|
import { join } from "node:path";
|
|
import type { ToolContext } from "../agent/tools.js";
|
|
import { downloadMessageFile, type FeishuRuntime } from "./client.js";
|
|
|
|
export interface FeishuMessageResourceArgs {
|
|
readonly messageId: string;
|
|
readonly fileKey: string;
|
|
readonly resourceType: "image" | "file";
|
|
}
|
|
|
|
export interface DownloadedFeishuMessageResource extends FeishuMessageResourceArgs {
|
|
readonly path: string;
|
|
}
|
|
|
|
type DownloadContext = Pick<ToolContext, "boundChatId" | "workspaceDir"> & { readonly workspaceRoot: string };
|
|
|
|
interface MessageLookupResult {
|
|
readonly data?: {
|
|
readonly message?: { readonly chat_id?: string | undefined } | undefined;
|
|
readonly items?: ReadonlyArray<{ readonly chat_id?: string | undefined }> | undefined;
|
|
} | undefined;
|
|
}
|
|
|
|
/** Download one resource after proving its message belongs to the run's bound chat. */
|
|
export async function downloadFeishuMessageResource(
|
|
args: FeishuMessageResourceArgs,
|
|
context: DownloadContext,
|
|
rt: FeishuRuntime,
|
|
): Promise<DownloadedFeishuMessageResource> {
|
|
const response = await rt.client.im.v1.message.get({
|
|
path: { message_id: args.messageId },
|
|
}) as MessageLookupResult;
|
|
const message = response.data?.message ?? response.data?.items?.[0];
|
|
if (message === undefined) {
|
|
throw new Error(`Feishu message not found: ${args.messageId}`);
|
|
}
|
|
if (message.chat_id !== context.boundChatId) {
|
|
rt.logger.warn(
|
|
{ messageId: args.messageId, boundChatId: context.boundChatId, returnedChatId: message.chat_id },
|
|
"Feishu resource download refused outside bound chat",
|
|
);
|
|
throw new Error(
|
|
`refused Feishu resource download: message ${args.messageId} is not in the current project's bound chat`,
|
|
);
|
|
}
|
|
|
|
const extension = args.resourceType === "image" ? ".png" : ".bin";
|
|
const workspaceRelativePath = join(
|
|
".cph",
|
|
"inbox",
|
|
`feishu-${args.resourceType}-${randomUUID()}${extension}`,
|
|
);
|
|
const savePath = await downloadMessageFile(
|
|
rt,
|
|
args.messageId,
|
|
args.fileKey,
|
|
context.workspaceRoot,
|
|
context.workspaceDir,
|
|
workspaceRelativePath,
|
|
args.resourceType,
|
|
);
|
|
return { ...args, path: savePath };
|
|
}
|