refactor: centralize message provider normalization
This commit is contained in:
@@ -7,6 +7,7 @@ import {
|
|||||||
} from "../config/sessions.js";
|
} from "../config/sessions.js";
|
||||||
import { callGateway, randomIdempotencyKey } from "../gateway/call.js";
|
import { callGateway, randomIdempotencyKey } from "../gateway/call.js";
|
||||||
import type { RuntimeEnv } from "../runtime.js";
|
import type { RuntimeEnv } from "../runtime.js";
|
||||||
|
import { normalizeMessageProvider } from "../utils/message-provider.js";
|
||||||
import { agentCommand } from "./agent.js";
|
import { agentCommand } from "./agent.js";
|
||||||
|
|
||||||
type AgentGatewayResult = {
|
type AgentGatewayResult = {
|
||||||
@@ -85,12 +86,6 @@ function parseTimeoutSeconds(opts: {
|
|||||||
return raw;
|
return raw;
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeProvider(raw?: string): string | undefined {
|
|
||||||
const normalized = raw?.trim().toLowerCase();
|
|
||||||
if (!normalized) return undefined;
|
|
||||||
return normalized === "imsg" ? "imessage" : normalized;
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatPayloadForLog(payload: {
|
function formatPayloadForLog(payload: {
|
||||||
text?: string;
|
text?: string;
|
||||||
mediaUrls?: string[];
|
mediaUrls?: string[];
|
||||||
@@ -127,7 +122,7 @@ export async function agentViaGatewayCommand(
|
|||||||
sessionId: opts.sessionId,
|
sessionId: opts.sessionId,
|
||||||
});
|
});
|
||||||
|
|
||||||
const provider = normalizeProvider(opts.provider) ?? "whatsapp";
|
const provider = normalizeMessageProvider(opts.provider) ?? "whatsapp";
|
||||||
const idempotencyKey = opts.runId?.trim() || randomIdempotencyKey();
|
const idempotencyKey = opts.runId?.trim() || randomIdempotencyKey();
|
||||||
|
|
||||||
const response = await callGateway<GatewayAgentResponse>({
|
const response = await callGateway<GatewayAgentResponse>({
|
||||||
|
|||||||
@@ -56,6 +56,10 @@ import {
|
|||||||
import { resolveOutboundTarget } from "../infra/outbound/targets.js";
|
import { resolveOutboundTarget } from "../infra/outbound/targets.js";
|
||||||
import { defaultRuntime, type RuntimeEnv } from "../runtime.js";
|
import { defaultRuntime, type RuntimeEnv } from "../runtime.js";
|
||||||
import { resolveSendPolicy } from "../sessions/send-policy.js";
|
import { resolveSendPolicy } from "../sessions/send-policy.js";
|
||||||
|
import {
|
||||||
|
normalizeMessageProvider,
|
||||||
|
resolveMessageProvider,
|
||||||
|
} from "../utils/message-provider.js";
|
||||||
import { normalizeE164 } from "../utils.js";
|
import { normalizeE164 } from "../utils.js";
|
||||||
|
|
||||||
type AgentCommandOpts = {
|
type AgentCommandOpts = {
|
||||||
@@ -395,13 +399,10 @@ export async function agentCommand(
|
|||||||
let fallbackProvider = provider;
|
let fallbackProvider = provider;
|
||||||
let fallbackModel = model;
|
let fallbackModel = model;
|
||||||
try {
|
try {
|
||||||
const messageProvider =
|
const messageProvider = resolveMessageProvider(
|
||||||
opts.messageProvider?.trim().toLowerCase() ||
|
opts.messageProvider,
|
||||||
(() => {
|
opts.provider,
|
||||||
const raw = opts.provider?.trim().toLowerCase();
|
);
|
||||||
if (!raw) return undefined;
|
|
||||||
return raw === "imsg" ? "imessage" : raw;
|
|
||||||
})();
|
|
||||||
const fallbackResult = await runWithModelFallback({
|
const fallbackResult = await runWithModelFallback({
|
||||||
cfg,
|
cfg,
|
||||||
provider,
|
provider,
|
||||||
@@ -514,9 +515,8 @@ export async function agentCommand(
|
|||||||
const payloads = result.payloads ?? [];
|
const payloads = result.payloads ?? [];
|
||||||
const deliver = opts.deliver === true;
|
const deliver = opts.deliver === true;
|
||||||
const bestEffortDeliver = opts.bestEffortDeliver === true;
|
const bestEffortDeliver = opts.bestEffortDeliver === true;
|
||||||
const deliveryProviderRaw = (opts.provider ?? "whatsapp").toLowerCase();
|
|
||||||
const deliveryProvider =
|
const deliveryProvider =
|
||||||
deliveryProviderRaw === "imsg" ? "imessage" : deliveryProviderRaw;
|
normalizeMessageProvider(opts.provider) ?? "whatsapp";
|
||||||
|
|
||||||
const logDeliveryError = (err: unknown) => {
|
const logDeliveryError = (err: unknown) => {
|
||||||
const message = `Delivery failed (${deliveryProvider}${deliveryTarget ? ` to ${deliveryTarget}` : ""}): ${String(err)}`;
|
const message = `Delivery failed (${deliveryProvider}${deliveryTarget ? ` to ${deliveryTarget}` : ""}): ${String(err)}`;
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
} from "../infra/outbound/format.js";
|
} from "../infra/outbound/format.js";
|
||||||
import { resolveOutboundTarget } from "../infra/outbound/targets.js";
|
import { resolveOutboundTarget } from "../infra/outbound/targets.js";
|
||||||
import type { RuntimeEnv } from "../runtime.js";
|
import type { RuntimeEnv } from "../runtime.js";
|
||||||
|
import { normalizeMessageProvider } from "../utils/message-provider.js";
|
||||||
|
|
||||||
export async function sendCommand(
|
export async function sendCommand(
|
||||||
opts: {
|
opts: {
|
||||||
@@ -26,8 +27,7 @@ export async function sendCommand(
|
|||||||
deps: CliDeps,
|
deps: CliDeps,
|
||||||
runtime: RuntimeEnv,
|
runtime: RuntimeEnv,
|
||||||
) {
|
) {
|
||||||
const providerRaw = (opts.provider ?? "whatsapp").toLowerCase();
|
const provider = normalizeMessageProvider(opts.provider) ?? "whatsapp";
|
||||||
const provider = providerRaw === "imsg" ? "imessage" : providerRaw;
|
|
||||||
|
|
||||||
if (opts.dryRun) {
|
if (opts.dryRun) {
|
||||||
runtime.log(
|
runtime.log(
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { randomUUID } from "node:crypto";
|
import { randomUUID } from "node:crypto";
|
||||||
import type { IncomingMessage } from "node:http";
|
import type { IncomingMessage } from "node:http";
|
||||||
import type { ClawdbotConfig } from "../config/config.js";
|
import type { ClawdbotConfig } from "../config/config.js";
|
||||||
|
import { normalizeMessageProvider } from "../utils/message-provider.js";
|
||||||
import {
|
import {
|
||||||
type HookMappingResolved,
|
type HookMappingResolved,
|
||||||
resolveHookMappings,
|
resolveHookMappings,
|
||||||
@@ -174,20 +175,22 @@ export function normalizeAgentPayload(
|
|||||||
? sessionKeyRaw.trim()
|
? sessionKeyRaw.trim()
|
||||||
: `hook:${idFactory()}`;
|
: `hook:${idFactory()}`;
|
||||||
const providerRaw = payload.provider;
|
const providerRaw = payload.provider;
|
||||||
|
const providerNormalized =
|
||||||
|
typeof providerRaw === "string"
|
||||||
|
? normalizeMessageProvider(providerRaw)
|
||||||
|
: undefined;
|
||||||
const provider =
|
const provider =
|
||||||
providerRaw === "whatsapp" ||
|
providerNormalized === "whatsapp" ||
|
||||||
providerRaw === "telegram" ||
|
providerNormalized === "telegram" ||
|
||||||
providerRaw === "discord" ||
|
providerNormalized === "discord" ||
|
||||||
providerRaw === "slack" ||
|
providerNormalized === "slack" ||
|
||||||
providerRaw === "signal" ||
|
providerNormalized === "signal" ||
|
||||||
providerRaw === "imessage" ||
|
providerNormalized === "imessage" ||
|
||||||
providerRaw === "last"
|
providerNormalized === "last"
|
||||||
? providerRaw
|
? providerNormalized
|
||||||
: providerRaw === "imsg"
|
: providerRaw === undefined
|
||||||
? "imessage"
|
? "last"
|
||||||
: providerRaw === undefined
|
: null;
|
||||||
? "last"
|
|
||||||
: null;
|
|
||||||
if (provider === null) {
|
if (provider === null) {
|
||||||
return {
|
return {
|
||||||
ok: false,
|
ok: false,
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
import { registerAgentRunContext } from "../../infra/agent-events.js";
|
import { registerAgentRunContext } from "../../infra/agent-events.js";
|
||||||
import { defaultRuntime } from "../../runtime.js";
|
import { defaultRuntime } from "../../runtime.js";
|
||||||
import { resolveSendPolicy } from "../../sessions/send-policy.js";
|
import { resolveSendPolicy } from "../../sessions/send-policy.js";
|
||||||
|
import { normalizeMessageProvider } from "../../utils/message-provider.js";
|
||||||
import { normalizeE164 } from "../../utils.js";
|
import { normalizeE164 } from "../../utils.js";
|
||||||
import {
|
import {
|
||||||
type AgentWaitParams,
|
type AgentWaitParams,
|
||||||
@@ -138,15 +139,8 @@ export const agentHandlers: GatewayRequestHandlers = {
|
|||||||
|
|
||||||
const runId = idem;
|
const runId = idem;
|
||||||
|
|
||||||
const requestedProviderRaw =
|
|
||||||
typeof request.provider === "string" ? request.provider.trim() : "";
|
|
||||||
const requestedProviderNormalized = requestedProviderRaw
|
|
||||||
? requestedProviderRaw.toLowerCase()
|
|
||||||
: "last";
|
|
||||||
const requestedProvider =
|
const requestedProvider =
|
||||||
requestedProviderNormalized === "imsg"
|
normalizeMessageProvider(request.provider) ?? "last";
|
||||||
? "imessage"
|
|
||||||
: requestedProviderNormalized;
|
|
||||||
|
|
||||||
const lastProvider = sessionEntry?.lastProvider;
|
const lastProvider = sessionEntry?.lastProvider;
|
||||||
const lastTo =
|
const lastTo =
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { sendMessageSignal } from "../../signal/index.js";
|
|||||||
import { sendMessageSlack } from "../../slack/send.js";
|
import { sendMessageSlack } from "../../slack/send.js";
|
||||||
import { sendMessageTelegram } from "../../telegram/send.js";
|
import { sendMessageTelegram } from "../../telegram/send.js";
|
||||||
import { resolveTelegramToken } from "../../telegram/token.js";
|
import { resolveTelegramToken } from "../../telegram/token.js";
|
||||||
|
import { normalizeMessageProvider } from "../../utils/message-provider.js";
|
||||||
import { resolveDefaultWhatsAppAccountId } from "../../web/accounts.js";
|
import { resolveDefaultWhatsAppAccountId } from "../../web/accounts.js";
|
||||||
import { sendMessageWhatsApp, sendPollWhatsApp } from "../../web/outbound.js";
|
import { sendMessageWhatsApp, sendPollWhatsApp } from "../../web/outbound.js";
|
||||||
import {
|
import {
|
||||||
@@ -51,8 +52,7 @@ export const sendHandlers: GatewayRequestHandlers = {
|
|||||||
}
|
}
|
||||||
const to = request.to.trim();
|
const to = request.to.trim();
|
||||||
const message = request.message.trim();
|
const message = request.message.trim();
|
||||||
const providerRaw = (request.provider ?? "whatsapp").toLowerCase();
|
const provider = normalizeMessageProvider(request.provider) ?? "whatsapp";
|
||||||
const provider = providerRaw === "imsg" ? "imessage" : providerRaw;
|
|
||||||
try {
|
try {
|
||||||
if (provider === "telegram") {
|
if (provider === "telegram") {
|
||||||
const cfg = loadConfig();
|
const cfg = loadConfig();
|
||||||
@@ -220,8 +220,7 @@ export const sendHandlers: GatewayRequestHandlers = {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const to = request.to.trim();
|
const to = request.to.trim();
|
||||||
const providerRaw = (request.provider ?? "whatsapp").toLowerCase();
|
const provider = normalizeMessageProvider(request.provider) ?? "whatsapp";
|
||||||
const provider = providerRaw === "imsg" ? "imessage" : providerRaw;
|
|
||||||
if (provider !== "whatsapp" && provider !== "discord") {
|
if (provider !== "whatsapp" && provider !== "discord") {
|
||||||
respond(
|
respond(
|
||||||
false,
|
false,
|
||||||
|
|||||||
@@ -21,6 +21,11 @@ import {
|
|||||||
|
|
||||||
installGatewayTestHooks();
|
installGatewayTestHooks();
|
||||||
|
|
||||||
|
function expectProviders(call: Record<string, unknown>, provider: string) {
|
||||||
|
expect(call.provider).toBe(provider);
|
||||||
|
expect(call.messageProvider).toBe(provider);
|
||||||
|
}
|
||||||
|
|
||||||
describe("gateway server agent", () => {
|
describe("gateway server agent", () => {
|
||||||
test("agent falls back to allowFrom when lastTo is stale", async () => {
|
test("agent falls back to allowFrom when lastTo is stale", async () => {
|
||||||
testState.allowFrom = ["+436769770569"];
|
testState.allowFrom = ["+436769770569"];
|
||||||
@@ -57,8 +62,7 @@ describe("gateway server agent", () => {
|
|||||||
|
|
||||||
const spy = vi.mocked(agentCommand);
|
const spy = vi.mocked(agentCommand);
|
||||||
const call = spy.mock.calls.at(-1)?.[0] as Record<string, unknown>;
|
const call = spy.mock.calls.at(-1)?.[0] as Record<string, unknown>;
|
||||||
expect(call.provider).toBe("whatsapp");
|
expectProviders(call, "whatsapp");
|
||||||
expect(call.messageProvider).toBe("whatsapp");
|
|
||||||
expect(call.to).toBe("+436769770569");
|
expect(call.to).toBe("+436769770569");
|
||||||
expect(call.sessionId).toBe("sess-main-stale");
|
expect(call.sessionId).toBe("sess-main-stale");
|
||||||
|
|
||||||
@@ -138,7 +142,7 @@ describe("gateway server agent", () => {
|
|||||||
|
|
||||||
const spy = vi.mocked(agentCommand);
|
const spy = vi.mocked(agentCommand);
|
||||||
const call = spy.mock.calls.at(-1)?.[0] as Record<string, unknown>;
|
const call = spy.mock.calls.at(-1)?.[0] as Record<string, unknown>;
|
||||||
expect(call.provider).toBe("whatsapp");
|
expectProviders(call, "whatsapp");
|
||||||
expect(call.messageProvider).toBe("whatsapp");
|
expect(call.messageProvider).toBe("whatsapp");
|
||||||
expect(call.to).toBe("+1555");
|
expect(call.to).toBe("+1555");
|
||||||
expect(call.deliver).toBe(true);
|
expect(call.deliver).toBe(true);
|
||||||
@@ -183,8 +187,7 @@ describe("gateway server agent", () => {
|
|||||||
|
|
||||||
const spy = vi.mocked(agentCommand);
|
const spy = vi.mocked(agentCommand);
|
||||||
const call = spy.mock.calls.at(-1)?.[0] as Record<string, unknown>;
|
const call = spy.mock.calls.at(-1)?.[0] as Record<string, unknown>;
|
||||||
expect(call.provider).toBe("telegram");
|
expectProviders(call, "telegram");
|
||||||
expect(call.messageProvider).toBe("telegram");
|
|
||||||
expect(call.to).toBe("123");
|
expect(call.to).toBe("123");
|
||||||
expect(call.deliver).toBe(true);
|
expect(call.deliver).toBe(true);
|
||||||
expect(call.bestEffortDeliver).toBe(true);
|
expect(call.bestEffortDeliver).toBe(true);
|
||||||
@@ -228,8 +231,7 @@ describe("gateway server agent", () => {
|
|||||||
|
|
||||||
const spy = vi.mocked(agentCommand);
|
const spy = vi.mocked(agentCommand);
|
||||||
const call = spy.mock.calls.at(-1)?.[0] as Record<string, unknown>;
|
const call = spy.mock.calls.at(-1)?.[0] as Record<string, unknown>;
|
||||||
expect(call.provider).toBe("discord");
|
expectProviders(call, "discord");
|
||||||
expect(call.messageProvider).toBe("discord");
|
|
||||||
expect(call.to).toBe("channel:discord-123");
|
expect(call.to).toBe("channel:discord-123");
|
||||||
expect(call.deliver).toBe(true);
|
expect(call.deliver).toBe(true);
|
||||||
expect(call.bestEffortDeliver).toBe(true);
|
expect(call.bestEffortDeliver).toBe(true);
|
||||||
@@ -273,7 +275,7 @@ describe("gateway server agent", () => {
|
|||||||
|
|
||||||
const spy = vi.mocked(agentCommand);
|
const spy = vi.mocked(agentCommand);
|
||||||
const call = spy.mock.calls.at(-1)?.[0] as Record<string, unknown>;
|
const call = spy.mock.calls.at(-1)?.[0] as Record<string, unknown>;
|
||||||
expect(call.provider).toBe("signal");
|
expectProviders(call, "signal");
|
||||||
expect(call.to).toBe("+15551234567");
|
expect(call.to).toBe("+15551234567");
|
||||||
expect(call.deliver).toBe(true);
|
expect(call.deliver).toBe(true);
|
||||||
expect(call.bestEffortDeliver).toBe(true);
|
expect(call.bestEffortDeliver).toBe(true);
|
||||||
@@ -318,7 +320,7 @@ describe("gateway server agent", () => {
|
|||||||
|
|
||||||
const spy = vi.mocked(agentCommand);
|
const spy = vi.mocked(agentCommand);
|
||||||
const call = spy.mock.calls.at(-1)?.[0] as Record<string, unknown>;
|
const call = spy.mock.calls.at(-1)?.[0] as Record<string, unknown>;
|
||||||
expect(call.provider).toBe("whatsapp");
|
expectProviders(call, "whatsapp");
|
||||||
expect(call.to).toBe("+1555");
|
expect(call.to).toBe("+1555");
|
||||||
expect(call.deliver).toBe(true);
|
expect(call.deliver).toBe(true);
|
||||||
expect(call.bestEffortDeliver).toBe(true);
|
expect(call.bestEffortDeliver).toBe(true);
|
||||||
|
|||||||
16
src/utils/message-provider.ts
Normal file
16
src/utils/message-provider.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
export function normalizeMessageProvider(
|
||||||
|
raw?: string | null,
|
||||||
|
): string | undefined {
|
||||||
|
const normalized = raw?.trim().toLowerCase();
|
||||||
|
if (!normalized) return undefined;
|
||||||
|
return normalized === "imsg" ? "imessage" : normalized;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resolveMessageProvider(
|
||||||
|
primary?: string | null,
|
||||||
|
fallback?: string | null,
|
||||||
|
): string | undefined {
|
||||||
|
return (
|
||||||
|
normalizeMessageProvider(primary) ?? normalizeMessageProvider(fallback)
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user