feat: complete remaining capabilities and hide integration status

This commit is contained in:
yu committed 2026-11-02 10:43:11 +08:00
1 parent 66169590de
commit 95fb07e876
20 files changed
+15102 -201

No files matched your search

+60
View File
@@ -0,0 +1,60 @@
import { getHandler, register } from "../engine/registry";
import { RuntimeError, type CapabilityHandler } from "../engine/types";
import { findImapMail, type NormalizedMail } from "./message";
function text(args: Record<string, unknown>, keys: string[], fallback = ""): string {
for (const key of keys) { const value = args[key]; if (value !== undefined && value !== null && value !== "") return String(value); }
return fallback;
}
function number(value: unknown, fallback = 1): number { const parsed = Number(value); return Number.isFinite(parsed) && parsed > 0 ? Math.floor(parsed) : fallback; }
function record(value: unknown): Record<string, unknown> { return value && typeof value === "object" ? value as Record<string, unknown> : (() => { try { return JSON.parse(String(value || "{}")); } catch { return {}; } })(); }
function list(value: unknown): string[] { if (Array.isArray(value)) return value.map(String); try { const parsed = JSON.parse(String(value || "[]")); return Array.isArray(parsed) ? parsed.map(String) : [String(parsed)]; } catch { return String(value || "").split(/[,,;;\s]+/).filter(Boolean); } }
function mergeAccount(args: Record<string, unknown>): Record<string, unknown> { return { ...record(args.account_obj || args.recvObject || args.target_obj), ...args }; }
async function imapClient(args: Record<string, unknown>) {
const { ImapFlow } = await import("imapflow"); const config = mergeAccount(args);
const client = new ImapFlow({ host: text(config, ["host", "hostname", "server_address", "server"]), port: number(config.port, 993), secure: ["true", "1", "True"].includes(text(config, ["ssl", "is_ssl", "secure"], "True")), auth: { user: text(config, ["account", "username", "user"]), pass: text(config, ["password", "pass"]) }, logger: false });
await client.connect(); return client;
}
async function withMailbox<T>(args: Record<string, unknown>, callback: (client: any) => Promise<T>): Promise<T> {
const client = await imapClient(args); const lock = await client.getMailboxLock(text(args, ["folder", "email_folder", "inbox_type", "folder_name"], "INBOX"));
try { return await callback(client); } finally { lock.release(); client.close(); }
}
async function searchMails(args: Record<string, unknown>): Promise<NormalizedMail[]> {
const normalized: Record<string, unknown> = { ...args, From_data: text(args, ["From_data", "From", "sender_key", "recipient_key"]), Subject_data: text(args, ["Subject_data", "Subject", "subject_key"]), Body_data: text(args, ["Body_data", "body_key"]) };
return await findImapMail(normalized, { id: "mail-remaining", componentId: "mail-remaining" });
}
const connect: CapabilityHandler = async (_ctx, _node, args) => { const config = mergeAccount(args); await imapClient(config).then((client) => client.close()); return { kind: "imap", ...config }; };
register(["Mail_RecvConnect", "Mail_UnionConnect", "Mail_ExConnect"], connect);
register("Mail_RecvClose", async () => ({ closed: true }));
register("Mail_RecvGetList", async (_ctx, _node, args) => await searchMails(args));
register("Mail_ExSearchMail", async (_ctx, _node, args) => await searchMails(args));
register("Mail_RecvMailGet", async (_ctx, _node, args) => {
const mails = await searchMails(args); const wanted = number(args.limit || args.index, 1); const mail = mails[wanted - 1];
if (!mail) throw new RuntimeError("not_found", "未找到第 " + wanted + " 封邮件"); return mail;
});
register("Mail_RecvCount", async (_ctx, _node, args) => await withMailbox(args, async (client) => client.mailbox.exists));
async function setSeen(args: Record<string, unknown>, seen: boolean) {
return await withMailbox(args, async (client) => { const sequence = String(number(args.index || args.limit, 1)); const flag = String.fromCharCode(92) + "Seen";
return seen ? await client.messageFlagsAdd(sequence, [flag]) : await client.messageFlagsRemove(sequence, [flag]); });
}
register("Mail_MarkSeen", async (_ctx, _node, args) => setSeen(args, true));
register("Mail_UnmarkSeen", async (_ctx, _node, args) => setSeen(args, false));
register(["Mail_DoMoveMail", "Mail_OperateMail", "Mail_ExOperateMail", "Mail_DoMoveMail_v1"], async (_ctx, _node, args) => {
const destination = text(args, ["DstFile", "destination", "folder_name", "email_folder"], "Archive");
return await withMailbox(args, async (client) => await client.messageMove(String(number(args.index || args.limit, 1)), destination));
});
async function invoke(id: string, args: Record<string, unknown>) { const handler = getHandler(id); if (!handler) throw new RuntimeError("capability_not_implemented", id + " 不可用"); return handler; }
register("Mail_DoSendMail", async (ctx, node, args) => {
const handler = await invoke("Mail_Send", args); const config = mergeAccount(args);
return await handler(ctx, node, { ...args, host: text(config, ["host", "server_address"]), account: text(config, ["Account", "account", "username"]), password: text(config, ["password"]), to: list(args.RecvByEmail), cc: list(args.CC), bcc: list(args.BCC), subject: text(args, ["Subject"]), content: text(args, ["Body", "body"]), attachment: text(args, ["filePath", "attachment"]) }, {} as never);
});
register("Mail_DoSendMail_v1", async (ctx, node, args) => { const handler = await invoke("Mail_DoSendMail", args); return await handler(ctx, node, { ...args, account_obj: mergeAccount(args), RecvByEmail: args.RecvByEmail || args.target_email, CC: args.CC || args.cc, Subject: args.Subject || args.subject, Body: args.Body || args.body || args.content, filePath: args.filePath || args.attachment }, {} as never); });
register("Mail_ExSendMail", async (ctx, node, args) => { const handler = await invoke("Mail_Send", args);
return await handler(ctx, node, { ...args, to: list(args.target_email), cc: list(args.cc), subject: text(args, ["subject"]), content: text(args, ["body"]), attachment: text(args, ["filePath", "attachment"]) }, {} as never);
});
register("Mail_DoReplyMail", async (ctx, node, args) => { const handler = await invoke("Mail_ReplyMail_v2", args);
return await handler(ctx, node, { ...args, subject: text(args, ["Subject"], "Re:"), content: text(args, ["ReplyBody", "content"]), attachment: text(args, ["filePath", "attachment"]) }, {} as never);
});
register("Mail_DoReplyMail_v1", async (ctx, node, args) => { const handler = await invoke("Mail_ReplyMail_v2", args);
return await handler(ctx, node, { ...args, subject: text(args, ["subject"], "Re:"), content: text(args, ["content", "ReplyBody"]), attachment: text(args, ["attachment"]) }, {} as never);
});