


























@@ -16,13 +16,10 @@ import { extractToolSend } from "openclaw/plugin-sdk/tool-send";
1616import { resolveIMessageAccount } from "./accounts.js";
1717import { IMESSAGE_ACTION_NAMES, IMESSAGE_ACTIONS } from "./actions-contract.js";
1818import { DEFAULT_IMESSAGE_PROBE_TIMEOUT_MS } from "./constants.js";
19+import { describeIMessageMessageTool } from "./message-tool-api.js";
1920import { findLatestIMessageEntryForChat, type IMessageChatContext } from "./monitor-reply-cache.js";
2021import { getCachedIMessagePrivateApiStatus } from "./probe.js";
21-import {
22-inferIMessageTargetChatType,
23-parseIMessageTarget,
24-type IMessageTarget,
25-} from "./targets.js";
22+import { parseIMessageTarget, type IMessageTarget } from "./targets.js";
26232724const loadIMessageActionsRuntime = createLazyRuntimeNamedExport(
2825() => import("./actions.runtime.js"),
@@ -35,20 +32,6 @@ const SUPPORTED_ACTIONS = new Set<ChannelMessageActionName>([
3532 ...IMESSAGE_ACTION_NAMES,
3633"upload-file",
3734]);
38-const PRIVATE_API_ACTIONS = new Set<ChannelMessageActionName>([
39-"react",
40-"edit",
41-"unsend",
42-"reply",
43-"sendWithEffect",
44-"renameGroup",
45-"setGroupIcon",
46-"addParticipant",
47-"removeParticipant",
48-"leaveGroup",
49-"sendAttachment",
50-]);
51-5235function readMessageText(params: Record<string, unknown>): string | undefined {
5336return readStringParam(params, "text") ?? readStringParam(params, "message");
5437}
@@ -78,16 +61,6 @@ function readMessageIdWithChatFallback(
7861return readStringParam(params, "messageId", { required: true });
7962}
806381-function isGroupTarget(raw?: string | null): boolean {
82-// Defer to the canonical target classifier so action gating and the
83-// routing layer can't drift apart on edge cases (URI-encoded targets,
84-// service prefixes, etc.).
85-if (!raw) {
86-return false;
87-}
88-return inferIMessageTargetChatType(raw) === "group";
89-}
90-9164type IMessageActionsRuntime = Awaited<ReturnType<typeof loadIMessageActionsRuntime>>;
92659366async function resolveChatGuid(params: {
@@ -329,58 +302,42 @@ function effectIdFromParam(raw?: string): string | undefined {
329302);
330303}
331304305+function assertActionEnabled(
306+action: ChannelMessageActionName,
307+actionsConfig: Record<string, boolean | undefined> | undefined,
308+): void {
309+const canonicalAction = action === "upload-file" ? "sendAttachment" : action;
310+const spec = IMESSAGE_ACTIONS[canonicalAction as keyof typeof IMESSAGE_ACTIONS];
311+if (!spec?.gate || !createActionGate(actionsConfig)(spec.gate)) {
312+throw new Error(`iMessage ${action} is disabled in config.`);
313+}
314+}
315+332316export const imessageMessageActions: ChannelMessageActionAdapter = {
333-describeMessageTool: ({ cfg, accountId, currentChannelId }) => {
334-const account = resolveIMessageAccount({ cfg, accountId });
335-if (!account.enabled || !account.configured) {
336-return null;
337-}
338-const privateApiStatus = getCachedIMessagePrivateApiStatus(
339-account.config.cliPath?.trim() || "imsg",
340-);
341-const gate = createActionGate(account.config.actions);
342-const actions = new Set<ChannelMessageActionName>();
343-for (const action of IMESSAGE_ACTION_NAMES) {
344-const spec = IMESSAGE_ACTIONS[action];
345-if (!spec?.gate || !gate(spec.gate)) {
346-continue;
347-}
348-if (privateApiStatus?.available === false && PRIVATE_API_ACTIONS.has(action)) {
349-continue;
350-}
351-if (
352-action === "edit" &&
353-privateApiStatus?.selectors &&
354-!privateApiStatus.selectors.editMessage &&
355-!privateApiStatus.selectors.editMessageItem
356-) {
357-continue;
358-}
359-if (action === "unsend" && privateApiStatus?.selectors?.retractMessagePart !== true) {
360-continue;
361-}
362-actions.add(action);
363-}
364-if (!isGroupTarget(currentChannelId)) {
365-for (const action of IMESSAGE_ACTION_NAMES) {
366-if ("groupOnly" in IMESSAGE_ACTIONS[action] && IMESSAGE_ACTIONS[action].groupOnly) {
367-actions.delete(action);
368-}
369-}
370-}
371-if (actions.delete("sendAttachment")) {
372-actions.add("upload-file");
373-}
374-return { actions: Array.from(actions) };
375-},
317+describeMessageTool: describeIMessageMessageTool,
376318supportsAction: ({ action }) => SUPPORTED_ACTIONS.has(action),
319+messageActionTargetAliases: {
320+react: { aliases: ["chatGuid", "chatIdentifier", "chatId"] },
321+edit: { aliases: ["chatGuid", "chatIdentifier", "chatId", "messageId"] },
322+unsend: { aliases: ["chatGuid", "chatIdentifier", "chatId", "messageId"] },
323+reply: { aliases: ["chatGuid", "chatIdentifier", "chatId", "messageId"] },
324+sendWithEffect: { aliases: ["chatGuid", "chatIdentifier", "chatId"] },
325+sendAttachment: { aliases: ["chatGuid", "chatIdentifier", "chatId"] },
326+"upload-file": { aliases: ["chatGuid", "chatIdentifier", "chatId"] },
327+renameGroup: { aliases: ["chatGuid", "chatIdentifier", "chatId"] },
328+setGroupIcon: { aliases: ["chatGuid", "chatIdentifier", "chatId"] },
329+addParticipant: { aliases: ["chatGuid", "chatIdentifier", "chatId"] },
330+removeParticipant: { aliases: ["chatGuid", "chatIdentifier", "chatId"] },
331+leaveGroup: { aliases: ["chatGuid", "chatIdentifier", "chatId"] },
332+},
377333extractToolSend: ({ args }) => extractToolSend(args, "sendMessage"),
378334handleAction: async ({ action, params, cfg, accountId, toolContext }) => {
379335const runtime = await loadIMessageActionsRuntime();
380336const account = resolveIMessageAccount({
381337 cfg,
382338accountId: accountId ?? undefined,
383339});
340+assertActionEnabled(action, account.config.actions);
384341const cliPathForProbe = account.config.cliPath?.trim() || "imsg";
385342let privateApiStatus = getCachedIMessagePrivateApiStatus(cliPathForProbe);
386343const assertPrivateApiEnabled = async () => {
@@ -607,7 +564,7 @@ export const imessageMessageActions: ChannelMessageActionAdapter = {
607564if (action === "sendAttachment" || action === "upload-file") {
608565await assertPrivateApiEnabled();
609566const filename = readStringParam(params, "filename", { required: true });
610-const asVoice = readBooleanParam(params, "asVoice");
567+const asVoice = readBooleanParam(params, "asVoice") ?? readBooleanParam(params, "as_voice");
611568const resolvedChatGuid = await chatGuid();
612569const result = await runtime.sendAttachment({
613570chatGuid: resolvedChatGuid,
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。