diff --git a/src/auto-reply/envelope.ts b/src/auto-reply/envelope.ts index 000a1ecdf..a3e0ba1fe 100644 --- a/src/auto-reply/envelope.ts +++ b/src/auto-reply/envelope.ts @@ -59,6 +59,27 @@ export function formatInboundEnvelope(params: { }); } +export function formatInboundFromLabel(params: { + isGroup: boolean; + groupLabel?: string; + groupId?: string; + directLabel: string; + directId?: string; + groupFallback?: string; +}): string { + // Keep envelope headers compact: group labels include id, DMs only add id when it differs. + if (params.isGroup) { + const label = params.groupLabel?.trim() || params.groupFallback || "Group"; + const id = params.groupId?.trim(); + return id ? `${label} id:${id}` : label; + } + + const directLabel = params.directLabel.trim(); + const directId = params.directId?.trim(); + if (!directId || directId === directLabel) return directLabel; + return `${directLabel} id:${directId}`; +} + export function formatThreadStarterEnvelope(params: { channel: string; author?: string; diff --git a/src/auto-reply/reply/inbound-sender-meta.test.ts b/src/auto-reply/reply/inbound-sender-meta.test.ts index 9b1208223..2bc8d3d86 100644 --- a/src/auto-reply/reply/inbound-sender-meta.test.ts +++ b/src/auto-reply/reply/inbound-sender-meta.test.ts @@ -46,4 +46,11 @@ describe("formatInboundBodyWithSenderMeta", () => { const ctx: MsgContext = { ChatType: "group", SenderName: "Alice", SenderId: "A1" }; expect(formatInboundBodyWithSenderMeta({ ctx, body: "Alice (A1): hi" })).toBe("Alice (A1): hi"); }); + + it("does not append when the sender prefix follows an envelope header", () => { + const ctx: MsgContext = { ChatType: "group", SenderName: "Alice", SenderId: "A1" }; + expect(formatInboundBodyWithSenderMeta({ ctx, body: "[Signal Group] Alice (A1): hi" })).toBe( + "[Signal Group] Alice (A1): hi", + ); + }); }); diff --git a/src/imessage/monitor/monitor-provider.ts b/src/imessage/monitor/monitor-provider.ts index 229eed5da..7add40386 100644 --- a/src/imessage/monitor/monitor-provider.ts +++ b/src/imessage/monitor/monitor-provider.ts @@ -11,7 +11,7 @@ import { } from "../../auto-reply/reply/response-prefix-template.js"; import { resolveTextChunkLimit } from "../../auto-reply/chunk.js"; import { hasControlCommand } from "../../auto-reply/command-detection.js"; -import { formatInboundEnvelope } from "../../auto-reply/envelope.js"; +import { formatInboundEnvelope, formatInboundFromLabel } from "../../auto-reply/envelope.js"; import { createInboundDebouncer, resolveInboundDebounceMs, @@ -383,13 +383,14 @@ export async function monitorIMessageProvider(opts: MonitorIMessageOpts = {}): P } const chatTarget = formatIMessageChatTarget(chatId); - // For groups: use chat name or just "Group" (channel "iMessage" is already shown). - // For DMs: keep headers compact; only add id: suffix if raw differs from normalized. - const fromLabel = isGroup - ? `${message.chat_name || "Group"} id:${chatId ?? "unknown"}` - : senderNormalized === sender - ? senderNormalized - : `${senderNormalized} id:${sender}`; + const fromLabel = formatInboundFromLabel({ + isGroup, + groupLabel: message.chat_name ?? undefined, + groupId: chatId !== undefined ? String(chatId) : "unknown", + groupFallback: "Group", + directLabel: senderNormalized, + directId: sender, + }); const body = formatInboundEnvelope({ channel: "iMessage", from: fromLabel, diff --git a/src/signal/monitor/event-handler.ts b/src/signal/monitor/event-handler.ts index e53179afa..d17841504 100644 --- a/src/signal/monitor/event-handler.ts +++ b/src/signal/monitor/event-handler.ts @@ -8,7 +8,7 @@ import { type ResponsePrefixContext, } from "../../auto-reply/reply/response-prefix-template.js"; import { hasControlCommand } from "../../auto-reply/command-detection.js"; -import { formatInboundEnvelope } from "../../auto-reply/envelope.js"; +import { formatInboundEnvelope, formatInboundFromLabel } from "../../auto-reply/envelope.js"; import { createInboundDebouncer, resolveInboundDebounceMs, @@ -65,13 +65,14 @@ export function createSignalEventHandler(deps: SignalEventHandlerDeps) { }; async function handleSignalInboundMessage(entry: SignalInboundEntry) { - // For groups: use group name or just "Group" (channel "Signal" is already shown). - // For DMs: keep headers compact; only add id: suffix if display differs from name. - const fromLabel = entry.isGroup - ? `${entry.groupName || "Group"} id:${entry.groupId}` - : entry.senderName === entry.senderDisplay - ? entry.senderName - : `${entry.senderName} id:${entry.senderDisplay}`; + const fromLabel = formatInboundFromLabel({ + isGroup: entry.isGroup, + groupLabel: entry.groupName ?? undefined, + groupId: entry.groupId ?? "unknown", + groupFallback: "Group", + directLabel: entry.senderName, + directId: entry.senderDisplay, + }); const body = formatInboundEnvelope({ channel: "Signal", from: fromLabel,