mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 02:30:24 +00:00
refactor: make OutboundSendDeps dynamic with channel-ID keys (#45517)
* refactor: make OutboundSendDeps dynamic with channel-ID keys
Replace hardcoded per-channel send fields (sendTelegram, sendDiscord,
etc.) with a dynamic index-signature type keyed by channel ID. This
unblocks moving channel implementations to extensions without breaking
the outbound dispatch contract.
- OutboundSendDeps and CliDeps are now { [channelId: string]: unknown }
- Each outbound adapter resolves its send fn via bracket access with cast
- Lazy-loading preserved via createLazySender with module cache
- Delete 6 deps-send-*.runtime.ts one-liner re-export files
- Harden guardrail scan against deleted-but-tracked files
* fix: preserve outbound send-deps compatibility
* style: fix formatting issues (import order, extra bracket, trailing whitespace)
* fix: resolve type errors from dynamic OutboundSendDeps in tests and extension
* fix: remove unused OutboundSendDeps import from deliver.test-helpers
This commit is contained in:
@@ -8,6 +8,7 @@ import {
|
||||
sendPollDiscord,
|
||||
sendWebhookMessageDiscord,
|
||||
} from "../../../discord/send.js";
|
||||
import { resolveOutboundSendDep } from "../../../infra/outbound/deliver.js";
|
||||
import type { OutboundIdentity } from "../../../infra/outbound/identity.js";
|
||||
import { normalizeDiscordOutboundTarget } from "../normalize/discord.js";
|
||||
import type { ChannelOutboundAdapter } from "../types.js";
|
||||
@@ -100,7 +101,8 @@ export const discordOutbound: ChannelOutboundAdapter = {
|
||||
return { channel: "discord", ...webhookResult };
|
||||
}
|
||||
}
|
||||
const send = deps?.sendDiscord ?? sendMessageDiscord;
|
||||
const send =
|
||||
resolveOutboundSendDep<typeof sendMessageDiscord>(deps, "discord") ?? sendMessageDiscord;
|
||||
const target = resolveDiscordOutboundTarget({ to, threadId });
|
||||
const result = await send(target, text, {
|
||||
verbose: false,
|
||||
@@ -123,7 +125,8 @@ export const discordOutbound: ChannelOutboundAdapter = {
|
||||
threadId,
|
||||
silent,
|
||||
}) => {
|
||||
const send = deps?.sendDiscord ?? sendMessageDiscord;
|
||||
const send =
|
||||
resolveOutboundSendDep<typeof sendMessageDiscord>(deps, "discord") ?? sendMessageDiscord;
|
||||
const target = resolveDiscordOutboundTarget({ to, threadId });
|
||||
const result = await send(target, text, {
|
||||
verbose: false,
|
||||
|
||||
@@ -22,7 +22,7 @@ describe("imessageOutbound", () => {
|
||||
text: "hello",
|
||||
accountId: "default",
|
||||
replyToId: "msg-123",
|
||||
deps: { sendIMessage },
|
||||
deps: { imessage: sendIMessage },
|
||||
});
|
||||
|
||||
expect(sendIMessage).toHaveBeenCalledWith(
|
||||
@@ -50,7 +50,7 @@ describe("imessageOutbound", () => {
|
||||
mediaLocalRoots: ["/tmp"],
|
||||
accountId: "acct-1",
|
||||
replyToId: "msg-456",
|
||||
deps: { sendIMessage },
|
||||
deps: { imessage: sendIMessage },
|
||||
});
|
||||
|
||||
expect(sendIMessage).toHaveBeenCalledWith(
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import { sendMessageIMessage } from "../../../imessage/send.js";
|
||||
import type { OutboundSendDeps } from "../../../infra/outbound/deliver.js";
|
||||
import { resolveOutboundSendDep, type OutboundSendDeps } from "../../../infra/outbound/deliver.js";
|
||||
import {
|
||||
createScopedChannelMediaMaxBytesResolver,
|
||||
createDirectTextMediaOutbound,
|
||||
} from "./direct-text-media.js";
|
||||
|
||||
function resolveIMessageSender(deps: OutboundSendDeps | undefined) {
|
||||
return deps?.sendIMessage ?? sendMessageIMessage;
|
||||
return (
|
||||
resolveOutboundSendDep<typeof sendMessageIMessage>(deps, "imessage") ?? sendMessageIMessage
|
||||
);
|
||||
}
|
||||
|
||||
export const imessageOutbound = createDirectTextMediaOutbound({
|
||||
|
||||
@@ -26,7 +26,7 @@ describe("signalOutbound", () => {
|
||||
to: "+15555550123",
|
||||
text: "hello",
|
||||
accountId: "work",
|
||||
deps: { sendSignal },
|
||||
deps: { signal: sendSignal },
|
||||
});
|
||||
|
||||
expect(sendSignal).toHaveBeenCalledWith(
|
||||
@@ -52,7 +52,7 @@ describe("signalOutbound", () => {
|
||||
mediaUrl: "https://example.com/file.jpg",
|
||||
mediaLocalRoots: ["/tmp/media"],
|
||||
accountId: "default",
|
||||
deps: { sendSignal },
|
||||
deps: { signal: sendSignal },
|
||||
});
|
||||
|
||||
expect(sendSignal).toHaveBeenCalledWith(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { OutboundSendDeps } from "../../../infra/outbound/deliver.js";
|
||||
import { resolveOutboundSendDep, type OutboundSendDeps } from "../../../infra/outbound/deliver.js";
|
||||
import { sendMessageSignal } from "../../../signal/send.js";
|
||||
import {
|
||||
createScopedChannelMediaMaxBytesResolver,
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
} from "./direct-text-media.js";
|
||||
|
||||
function resolveSignalSender(deps: OutboundSendDeps | undefined) {
|
||||
return deps?.sendSignal ?? sendMessageSignal;
|
||||
return resolveOutboundSendDep<typeof sendMessageSignal>(deps, "signal") ?? sendMessageSignal;
|
||||
}
|
||||
|
||||
export const signalOutbound = createDirectTextMediaOutbound({
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { resolveOutboundSendDep } from "../../../infra/outbound/deliver.js";
|
||||
import type { OutboundIdentity } from "../../../infra/outbound/identity.js";
|
||||
import { getGlobalHookRunner } from "../../../plugins/hook-runner-global.js";
|
||||
import { parseSlackBlocksInput } from "../../../slack/blocks-input.js";
|
||||
@@ -56,12 +57,13 @@ async function sendSlackOutboundMessage(params: {
|
||||
mediaLocalRoots?: readonly string[];
|
||||
blocks?: NonNullable<Parameters<typeof sendMessageSlack>[2]>["blocks"];
|
||||
accountId?: string | null;
|
||||
deps?: { sendSlack?: typeof sendMessageSlack } | null;
|
||||
deps?: { [channelId: string]: unknown } | null;
|
||||
replyToId?: string | null;
|
||||
threadId?: string | number | null;
|
||||
identity?: OutboundIdentity;
|
||||
}) {
|
||||
const send = params.deps?.sendSlack ?? sendMessageSlack;
|
||||
const send =
|
||||
resolveOutboundSendDep<typeof sendMessageSlack>(params.deps, "slack") ?? sendMessageSlack;
|
||||
// Use threadId fallback so routed tool notifications stay in the Slack thread.
|
||||
const threadTs =
|
||||
params.replyToId ?? (params.threadId != null ? String(params.threadId) : undefined);
|
||||
|
||||
@@ -15,7 +15,7 @@ describe("telegramOutbound", () => {
|
||||
accountId: "work",
|
||||
replyToId: "44",
|
||||
threadId: "55",
|
||||
deps: { sendTelegram },
|
||||
deps: { telegram: sendTelegram },
|
||||
});
|
||||
|
||||
expect(sendTelegram).toHaveBeenCalledWith(
|
||||
@@ -43,7 +43,7 @@ describe("telegramOutbound", () => {
|
||||
text: "<b>hello</b>",
|
||||
accountId: "work",
|
||||
threadId: "12345:99",
|
||||
deps: { sendTelegram },
|
||||
deps: { telegram: sendTelegram },
|
||||
});
|
||||
|
||||
expect(sendTelegram).toHaveBeenCalledWith(
|
||||
@@ -70,7 +70,7 @@ describe("telegramOutbound", () => {
|
||||
mediaUrl: "https://example.com/a.jpg",
|
||||
mediaLocalRoots: ["/tmp/media"],
|
||||
accountId: "default",
|
||||
deps: { sendTelegram },
|
||||
deps: { telegram: sendTelegram },
|
||||
});
|
||||
|
||||
expect(sendTelegram).toHaveBeenCalledWith(
|
||||
@@ -112,7 +112,7 @@ describe("telegramOutbound", () => {
|
||||
payload,
|
||||
mediaLocalRoots: ["/tmp/media"],
|
||||
accountId: "default",
|
||||
deps: { sendTelegram },
|
||||
deps: { telegram: sendTelegram },
|
||||
});
|
||||
|
||||
expect(sendTelegram).toHaveBeenCalledTimes(2);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { ReplyPayload } from "../../../auto-reply/types.js";
|
||||
import type { OutboundSendDeps } from "../../../infra/outbound/deliver.js";
|
||||
import { resolveOutboundSendDep, type OutboundSendDeps } from "../../../infra/outbound/deliver.js";
|
||||
import type { TelegramInlineButtons } from "../../../telegram/button-types.js";
|
||||
import { markdownToTelegramHtmlChunks } from "../../../telegram/format.js";
|
||||
import {
|
||||
@@ -30,7 +30,9 @@ function resolveTelegramSendContext(params: {
|
||||
accountId?: string;
|
||||
};
|
||||
} {
|
||||
const send = params.deps?.sendTelegram ?? sendMessageTelegram;
|
||||
const send =
|
||||
resolveOutboundSendDep<typeof sendMessageTelegram>(params.deps, "telegram") ??
|
||||
sendMessageTelegram;
|
||||
return {
|
||||
send,
|
||||
baseOpts: {
|
||||
|
||||
@@ -87,7 +87,7 @@ describe("telegramOutbound.sendPayload", () => {
|
||||
},
|
||||
},
|
||||
},
|
||||
deps: { sendTelegram },
|
||||
deps: { telegram: sendTelegram },
|
||||
});
|
||||
|
||||
expect(sendTelegram).toHaveBeenCalledTimes(1);
|
||||
@@ -121,7 +121,7 @@ describe("telegramOutbound.sendPayload", () => {
|
||||
},
|
||||
},
|
||||
},
|
||||
deps: { sendTelegram },
|
||||
deps: { telegram: sendTelegram },
|
||||
});
|
||||
|
||||
expect(sendTelegram).toHaveBeenCalledTimes(2);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { resolveOutboundSendDep } from "../../infra/outbound/deliver.js";
|
||||
import type { PluginRuntimeChannel } from "../../plugins/runtime/types-channel.js";
|
||||
import { escapeRegExp } from "../../utils.js";
|
||||
import { resolveWhatsAppOutboundTarget } from "../../whatsapp/resolve-outbound-target.js";
|
||||
@@ -66,7 +67,8 @@ export function createWhatsAppOutboundBase({
|
||||
if (skipEmptyText && !normalizedText) {
|
||||
return { channel: "whatsapp", messageId: "" };
|
||||
}
|
||||
const send = deps?.sendWhatsApp ?? sendMessageWhatsApp;
|
||||
const send =
|
||||
resolveOutboundSendDep<WhatsAppSendMessage>(deps, "whatsapp") ?? sendMessageWhatsApp;
|
||||
const result = await send(to, normalizedText, {
|
||||
verbose: false,
|
||||
cfg,
|
||||
@@ -85,7 +87,8 @@ export function createWhatsAppOutboundBase({
|
||||
deps,
|
||||
gifPlayback,
|
||||
}) => {
|
||||
const send = deps?.sendWhatsApp ?? sendMessageWhatsApp;
|
||||
const send =
|
||||
resolveOutboundSendDep<WhatsAppSendMessage>(deps, "whatsapp") ?? sendMessageWhatsApp;
|
||||
const result = await send(to, normalizeText(text), {
|
||||
verbose: false,
|
||||
cfg,
|
||||
|
||||
Reference in New Issue
Block a user