forked from bai/curriculum-project-hub
feat: add deployable alpha silo
This commit is contained in:
@@ -25,6 +25,7 @@ export interface FeishuConfig {
|
||||
export interface FeishuRuntime {
|
||||
readonly client: lark.Client;
|
||||
readonly logger: FastifyBaseLogger;
|
||||
readonly isListenerReady?: () => boolean;
|
||||
}
|
||||
|
||||
export interface OutboundPayload {
|
||||
@@ -494,6 +495,7 @@ export async function downloadMessageFile(
|
||||
workspaceDir: string,
|
||||
workspaceRelativePath: string,
|
||||
resourceType: "image" | "file",
|
||||
maxBytes?: number,
|
||||
): Promise<string> {
|
||||
try {
|
||||
return await withRetry(async () => {
|
||||
@@ -509,6 +511,7 @@ export async function downloadMessageFile(
|
||||
workspaceDir,
|
||||
workspaceRelativePath,
|
||||
source,
|
||||
maxBytes,
|
||||
);
|
||||
} catch (error) {
|
||||
source.destroy();
|
||||
@@ -911,20 +914,41 @@ export function createLarkClient(config: FeishuConfig): lark.Client {
|
||||
});
|
||||
}
|
||||
|
||||
export function startFeishuListenerWithClient(
|
||||
export async function startFeishuListenerWithClient(
|
||||
config: FeishuConfig,
|
||||
client: lark.Client,
|
||||
logger: FastifyBaseLogger,
|
||||
onMessage: (event: MessageReceiveEvent, rt: FeishuRuntime) => Promise<void>,
|
||||
onCardAction?: (event: CardActionEvent, rt: FeishuRuntime) => Promise<void>,
|
||||
): FeishuRuntime {
|
||||
onTerminalError?: (error: Error) => void,
|
||||
): Promise<FeishuRuntime> {
|
||||
let state: "STARTING" | "READY" | "FAILED" = "STARTING";
|
||||
let resolveReady!: () => void;
|
||||
let rejectReady!: (error: Error) => void;
|
||||
const ready = new Promise<void>((resolve, reject) => {
|
||||
resolveReady = resolve;
|
||||
rejectReady = reject;
|
||||
});
|
||||
const wsClient = new lark.WSClient({
|
||||
appId: config.appId,
|
||||
appSecret: config.appSecret,
|
||||
domain: lark.Domain.Feishu,
|
||||
loggerLevel: lark.LoggerLevel.info,
|
||||
handshakeTimeoutMs: 10_000,
|
||||
onReady: () => {
|
||||
state = "READY";
|
||||
resolveReady();
|
||||
logger.info("Feishu listener ready");
|
||||
},
|
||||
onError: (error) => {
|
||||
const wasStarting = state === "STARTING";
|
||||
state = "FAILED";
|
||||
logger.error({ err: error }, "Feishu listener terminal failure");
|
||||
if (wasStarting) rejectReady(error);
|
||||
else onTerminalError?.(error);
|
||||
},
|
||||
});
|
||||
const rt: FeishuRuntime = { client, logger };
|
||||
const rt: FeishuRuntime = { client, logger, isListenerReady: () => state === "READY" };
|
||||
const handlers: Record<string, (data: unknown) => Promise<void>> = {
|
||||
"im.message.receive_v1": async (data) => {
|
||||
try { await onMessage(data as MessageReceiveEvent, rt); }
|
||||
@@ -938,15 +962,25 @@ export function startFeishuListenerWithClient(
|
||||
.catch((e) => { logger.error({ err: e }, "feishu card action handler threw"); });
|
||||
};
|
||||
}
|
||||
void wsClient.start({ eventDispatcher: new lark.EventDispatcher({}).register(handlers) });
|
||||
await wsClient.start({ eventDispatcher: new lark.EventDispatcher({}).register(handlers) });
|
||||
let timeout: ReturnType<typeof setTimeout> | undefined;
|
||||
const startupTimeout = new Promise<never>((_resolve, reject) => {
|
||||
timeout = setTimeout(() => reject(new Error("Feishu listener did not become ready within 15 seconds")), 15_000);
|
||||
timeout.unref();
|
||||
});
|
||||
try {
|
||||
await Promise.race([ready, startupTimeout]);
|
||||
} finally {
|
||||
if (timeout !== undefined) clearTimeout(timeout);
|
||||
}
|
||||
return rt;
|
||||
}
|
||||
|
||||
export function startFeishuListener(
|
||||
export async function startFeishuListener(
|
||||
config: FeishuConfig,
|
||||
logger: FastifyBaseLogger,
|
||||
onMessage: (event: MessageReceiveEvent, rt: FeishuRuntime) => Promise<void>,
|
||||
onCardAction?: (event: CardActionEvent, rt: FeishuRuntime) => Promise<void>,
|
||||
): FeishuRuntime {
|
||||
): Promise<FeishuRuntime> {
|
||||
return startFeishuListenerWithClient(config, createLarkClient(config), logger, onMessage, onCardAction);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user