fix: unify inbound sender labels
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { formatAgentEnvelope } from "./envelope.js";
|
||||
import { formatAgentEnvelope, formatInboundEnvelope } from "./envelope.js";
|
||||
|
||||
describe("formatAgentEnvelope", () => {
|
||||
it("includes channel, from, ip, host, and timestamp", () => {
|
||||
@@ -43,3 +43,38 @@ describe("formatAgentEnvelope", () => {
|
||||
expect(body).toBe("[Telegram] hi");
|
||||
});
|
||||
});
|
||||
|
||||
describe("formatInboundEnvelope", () => {
|
||||
it("prefixes sender for non-direct chats", () => {
|
||||
const body = formatInboundEnvelope({
|
||||
channel: "Discord",
|
||||
from: "Guild #general",
|
||||
body: "hi",
|
||||
chatType: "channel",
|
||||
senderLabel: "Alice",
|
||||
});
|
||||
expect(body).toBe("[Discord Guild #general] Alice: hi");
|
||||
});
|
||||
|
||||
it("uses sender fields when senderLabel is missing", () => {
|
||||
const body = formatInboundEnvelope({
|
||||
channel: "Signal",
|
||||
from: "Signal Group id:123",
|
||||
body: "ping",
|
||||
chatType: "group",
|
||||
sender: { name: "Bob", id: "42" },
|
||||
});
|
||||
expect(body).toBe("[Signal Signal Group id:123] Bob (42): ping");
|
||||
});
|
||||
|
||||
it("keeps direct messages unprefixed", () => {
|
||||
const body = formatInboundEnvelope({
|
||||
channel: "iMessage",
|
||||
from: "+1555",
|
||||
body: "hello",
|
||||
chatType: "direct",
|
||||
senderLabel: "Alice",
|
||||
});
|
||||
expect(body).toBe("[iMessage +1555] hello");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import { normalizeChatType } from "../channels/chat-type.js";
|
||||
import { resolveSenderLabel, type SenderLabelParams } from "../channels/sender-label.js";
|
||||
|
||||
export type AgentEnvelopeParams = {
|
||||
channel: string;
|
||||
from?: string;
|
||||
@@ -35,6 +38,27 @@ export function formatAgentEnvelope(params: AgentEnvelopeParams): string {
|
||||
return `${header} ${params.body}`;
|
||||
}
|
||||
|
||||
export function formatInboundEnvelope(params: {
|
||||
channel: string;
|
||||
from: string;
|
||||
body: string;
|
||||
timestamp?: number | Date;
|
||||
chatType?: string;
|
||||
senderLabel?: string;
|
||||
sender?: SenderLabelParams;
|
||||
}): string {
|
||||
const chatType = normalizeChatType(params.chatType);
|
||||
const isDirect = !chatType || chatType === "direct";
|
||||
const resolvedSender = params.senderLabel?.trim() || resolveSenderLabel(params.sender ?? {});
|
||||
const body = !isDirect && resolvedSender ? `${resolvedSender}: ${params.body}` : params.body;
|
||||
return formatAgentEnvelope({
|
||||
channel: params.channel,
|
||||
from: params.from,
|
||||
timestamp: params.timestamp,
|
||||
body,
|
||||
});
|
||||
}
|
||||
|
||||
export function formatThreadStarterEnvelope(params: {
|
||||
channel: string;
|
||||
author?: string;
|
||||
|
||||
@@ -41,4 +41,11 @@ describe("formatInboundBodyWithSenderMeta", () => {
|
||||
"[X] hi\n[from: Alice (A1)]",
|
||||
);
|
||||
});
|
||||
|
||||
it("does not append when the body already includes a sender prefix", () => {
|
||||
const ctx: MsgContext = { ChatType: "group", SenderName: "Alice", SenderId: "A1" };
|
||||
expect(formatInboundBodyWithSenderMeta({ ctx, body: "Alice (A1): hi" })).toBe(
|
||||
"Alice (A1): hi",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,28 +1,43 @@
|
||||
import type { MsgContext } from "../templating.js";
|
||||
import { normalizeChatType } from "../../channels/chat-type.js";
|
||||
import { listSenderLabelCandidates, resolveSenderLabel } from "../../channels/sender-label.js";
|
||||
|
||||
export function formatInboundBodyWithSenderMeta(params: { body: string; ctx: MsgContext }): string {
|
||||
const body = params.body;
|
||||
if (!body.trim()) return body;
|
||||
const chatType = normalizeChatType(params.ctx.ChatType);
|
||||
if (!chatType || chatType === "direct") return body;
|
||||
if (hasSenderMetaLine(body)) return body;
|
||||
if (hasSenderMetaLine(body, params.ctx)) return body;
|
||||
|
||||
const senderLabel = formatSenderLabel(params.ctx);
|
||||
const senderLabel = resolveSenderLabel({
|
||||
name: params.ctx.SenderName,
|
||||
username: params.ctx.SenderUsername,
|
||||
tag: params.ctx.SenderTag,
|
||||
e164: params.ctx.SenderE164,
|
||||
id: params.ctx.SenderId,
|
||||
});
|
||||
if (!senderLabel) return body;
|
||||
|
||||
return `${body}\n[from: ${senderLabel}]`;
|
||||
}
|
||||
|
||||
function hasSenderMetaLine(body: string): boolean {
|
||||
return /(^|\n)\[from:/i.test(body);
|
||||
function hasSenderMetaLine(body: string, ctx: MsgContext): boolean {
|
||||
if (/(^|\n)\[from:/i.test(body)) return true;
|
||||
const candidates = listSenderLabelCandidates({
|
||||
name: ctx.SenderName,
|
||||
username: ctx.SenderUsername,
|
||||
tag: ctx.SenderTag,
|
||||
e164: ctx.SenderE164,
|
||||
id: ctx.SenderId,
|
||||
});
|
||||
if (candidates.length === 0) return false;
|
||||
return candidates.some((candidate) => {
|
||||
const escaped = escapeRegExp(candidate);
|
||||
const pattern = new RegExp(`(^|\\n)${escaped}:\\s`, "i");
|
||||
return pattern.test(body);
|
||||
});
|
||||
}
|
||||
|
||||
function formatSenderLabel(ctx: MsgContext): string | null {
|
||||
const senderName = ctx.SenderName?.trim();
|
||||
const senderId = (ctx.SenderE164?.trim() || ctx.SenderId?.trim()) ?? "";
|
||||
if (senderName && senderId && senderName !== senderId) {
|
||||
return `${senderName} (${senderId})`;
|
||||
}
|
||||
return senderName ?? (senderId || null);
|
||||
function escapeRegExp(value: string): string {
|
||||
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user