






















1-import { spawn } from "node:child_process";
21import {
32createMessageReceiptFromOutboundResults,
43type MessageReceipt,
@@ -53,7 +52,6 @@ type IMessageSendOpts = {
5352},
5453) => Promise<{ path: string; contentType?: string }>;
5554createClient?: (params: { cliPath: string; dbPath?: string }) => Promise<IMessageRpcClient>;
56-runCliJson?: (args: readonly string[]) => Promise<Record<string, unknown>>;
5755};
58565957export type IMessageSendResult = {
@@ -212,110 +210,6 @@ function resolveOutboundEchoScope(params: {
212210return `${params.accountId}:imessage:${params.target.to}`;
213211}
214212215-function buildIMessageCliJsonArgs(args: readonly string[], dbPath?: string): string[] {
216-const trimmedDbPath = dbPath?.trim();
217-return [...args, ...(trimmedDbPath ? ["--db", trimmedDbPath] : []), "--json"];
218-}
219-220-async function runIMessageCliJson(
221-cliPath: string,
222-dbPath: string | undefined,
223-args: readonly string[],
224-timeoutMs?: number,
225-): Promise<Record<string, unknown>> {
226-return await new Promise((resolve, reject) => {
227-const child = spawn(cliPath, buildIMessageCliJsonArgs(args, dbPath), {
228-stdio: ["ignore", "pipe", "pipe"],
229-});
230-let stdout = "";
231-let stderr = "";
232-let killEscalation: ReturnType<typeof setTimeout> | null = null;
233-const timer =
234-timeoutMs && timeoutMs > 0
235- ? setTimeout(() => {
236-child.kill("SIGTERM");
237-killEscalation = setTimeout(() => {
238-try {
239-child.kill("SIGKILL");
240-} catch {
241-// best-effort
242-}
243-}, 2000);
244-reject(new Error(`iMessage action timed out after ${timeoutMs}ms`));
245-}, timeoutMs)
246- : null;
247-child.stdout.setEncoding("utf8");
248-child.stderr.setEncoding("utf8");
249-child.stdout.on("data", (chunk) => {
250-stdout += chunk;
251-});
252-child.stderr.on("data", (chunk) => {
253-stderr += chunk;
254-});
255-child.on("error", (error) => {
256-if (timer) {
257-clearTimeout(timer);
258-}
259-if (killEscalation) {
260-clearTimeout(killEscalation);
261-}
262-reject(error);
263-});
264-child.on("close", (code) => {
265-if (timer) {
266-clearTimeout(timer);
267-}
268-if (killEscalation) {
269-clearTimeout(killEscalation);
270-}
271-const lines = stdout
272-.split(/\r?\n/u)
273-.map((line) => line.trim())
274-.filter(Boolean);
275-const last = lines.at(-1);
276-let parsed: Record<string, unknown> | null = null;
277-if (last) {
278-try {
279-const json = JSON.parse(last) as unknown;
280-if (json && typeof json === "object" && !Array.isArray(json)) {
281-parsed = json as Record<string, unknown>;
282-}
283-} catch {
284-// handled below
285-}
286-}
287-if (code === 0 && parsed) {
288-resolve(parsed);
289-return;
290-}
291-if (parsed && typeof parsed.error === "string" && parsed.error.trim()) {
292-reject(new Error(parsed.error.trim()));
293-return;
294-}
295-const detail = stderr.trim() || stdout.trim() || `imsg exited with code ${code}`;
296-reject(new Error(detail));
297-});
298-});
299-}
300-301-function stringValue(value: unknown): string | undefined {
302-return typeof value === "string" && value.trim() ? value.trim() : undefined;
303-}
304-305-async function resolveAttachmentChatGuid(params: {
306-target: ReturnType<typeof parseIMessageTarget>;
307-runCliJson: (args: readonly string[]) => Promise<Record<string, unknown>>;
308-}): Promise<string | null> {
309-if (params.target.kind === "chat_guid") {
310-return params.target.chatGuid;
311-}
312-if (params.target.kind !== "chat_id") {
313-return null;
314-}
315-const result = await params.runCliJson(["group", "--chat-id", String(params.target.chatId)]);
316-return stringValue(result.guid) ?? stringValue(result.chat_guid) ?? null;
317-}
318-319213export async function sendMessageIMessage(
320214to: string,
321215text: string,
@@ -384,56 +278,6 @@ export async function sendMessageIMessage(
384278}
385279const echoText = resolveOutboundEchoText(message, filePath ? mediaContentType : undefined);
386280const resolvedReplyToId = sanitizeReplyToId(opts.replyToId);
387-const runCliJson =
388-opts.runCliJson ??
389-((args: readonly string[]) => runIMessageCliJson(cliPath, dbPath, args, opts.timeoutMs));
390-391-if (filePath && !message.trim() && !resolvedReplyToId) {
392-const attachmentChatGuid = await resolveAttachmentChatGuid({ target, runCliJson });
393-if (attachmentChatGuid) {
394-const result = await runCliJson([
395-"send-attachment",
396-"--chat",
397-attachmentChatGuid,
398-"--file",
399-filePath,
400-"--transport",
401-"auto",
402-]);
403-const resolvedId = resolveMessageId(result);
404-const approvalBindingMessageId = resolveOutboundMessageGuid(result);
405-const messageId = resolvedId ?? (result?.ok || result?.success ? "ok" : "unknown");
406-const echoScope = resolveOutboundEchoScope({ accountId: account.accountId, target });
407-if (echoScope) {
408-rememberPersistedIMessageEcho({
409-scope: echoScope,
410-text: echoText,
411-messageId: resolvedId ?? undefined,
412-});
413-}
414-if (resolvedId) {
415-rememberIMessageReplyCache({
416-accountId: account.accountId,
417-messageId: resolvedId,
418-chatGuid: target.kind === "chat_guid" ? target.chatGuid : attachmentChatGuid,
419-chatId: target.kind === "chat_id" ? target.chatId : undefined,
420-timestamp: Date.now(),
421-isFromMe: true,
422-});
423-}
424-return {
425- messageId,
426- ...(approvalBindingMessageId ? { guid: approvalBindingMessageId } : {}),
427-sentText: message,
428- ...(echoText ? { echoText } : {}),
429-receipt: createIMessageSendReceipt({
430- messageId,
431- target,
432-kind: "media",
433-}),
434-};
435-}
436-}
437281const params: Record<string, unknown> = {
438282text: message,
439283service: service || "auto",
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。