mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-07 15:21:06 +00:00
fix(outbound): restore generic delivery and security seams
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { normalizeAnyChannelId } from "../channels/registry.js";
|
||||
import type { OutboundSendDeps } from "../infra/outbound/deliver.js";
|
||||
|
||||
/**
|
||||
@@ -6,23 +7,39 @@ import type { OutboundSendDeps } from "../infra/outbound/deliver.js";
|
||||
*/
|
||||
export type CliOutboundSendSource = { [channelId: string]: unknown };
|
||||
|
||||
const LEGACY_SOURCE_TO_CHANNEL = {
|
||||
sendMessageWhatsApp: "whatsapp",
|
||||
sendMessageTelegram: "telegram",
|
||||
sendMessageDiscord: "discord",
|
||||
sendMessageSlack: "slack",
|
||||
sendMessageSignal: "signal",
|
||||
sendMessageIMessage: "imessage",
|
||||
} as const;
|
||||
function normalizeLegacyChannelStem(raw: string): string {
|
||||
return raw
|
||||
.replace(/([a-z0-9])([A-Z])/g, "$1-$2")
|
||||
.replace(/_/g, "-")
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.replace(/-/g, "");
|
||||
}
|
||||
|
||||
const CHANNEL_TO_LEGACY_DEP_KEY = {
|
||||
whatsapp: "sendWhatsApp",
|
||||
telegram: "sendTelegram",
|
||||
discord: "sendDiscord",
|
||||
slack: "sendSlack",
|
||||
signal: "sendSignal",
|
||||
imessage: "sendIMessage",
|
||||
} as const;
|
||||
function resolveChannelIdFromLegacySourceKey(key: string): string | undefined {
|
||||
const match = key.match(/^sendMessage(.+)$/);
|
||||
if (!match) {
|
||||
return undefined;
|
||||
}
|
||||
const normalizedStem = normalizeLegacyChannelStem(match[1] ?? "");
|
||||
return normalizeAnyChannelId(normalizedStem) ?? (normalizedStem || undefined);
|
||||
}
|
||||
|
||||
function resolveLegacyDepKeysForChannel(channelId: string): string[] {
|
||||
const compact = channelId.replace(/[^a-z0-9]+/gi, "");
|
||||
if (!compact) {
|
||||
return [];
|
||||
}
|
||||
const pascal = compact.charAt(0).toUpperCase() + compact.slice(1);
|
||||
const keys = new Set<string>([`send${pascal}`]);
|
||||
if (pascal.startsWith("I") && pascal.length > 1) {
|
||||
keys.add(`sendI${pascal.slice(1)}`);
|
||||
}
|
||||
if (pascal.startsWith("Ms") && pascal.length > 2) {
|
||||
keys.add(`sendMS${pascal.slice(2)}`);
|
||||
}
|
||||
return [...keys];
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass CLI send sources through as-is — both CliOutboundSendSource and
|
||||
@@ -31,17 +48,26 @@ const CHANNEL_TO_LEGACY_DEP_KEY = {
|
||||
export function createOutboundSendDepsFromCliSource(deps: CliOutboundSendSource): OutboundSendDeps {
|
||||
const outbound: OutboundSendDeps = { ...deps };
|
||||
|
||||
for (const [legacySourceKey, channelId] of Object.entries(LEGACY_SOURCE_TO_CHANNEL)) {
|
||||
for (const legacySourceKey of Object.keys(deps)) {
|
||||
const channelId = resolveChannelIdFromLegacySourceKey(legacySourceKey);
|
||||
if (!channelId) {
|
||||
continue;
|
||||
}
|
||||
const sourceValue = deps[legacySourceKey];
|
||||
if (sourceValue !== undefined && outbound[channelId] === undefined) {
|
||||
outbound[channelId] = sourceValue;
|
||||
}
|
||||
}
|
||||
|
||||
for (const [channelId, legacyDepKey] of Object.entries(CHANNEL_TO_LEGACY_DEP_KEY)) {
|
||||
for (const channelId of Object.keys(outbound)) {
|
||||
const sourceValue = outbound[channelId];
|
||||
if (sourceValue !== undefined && outbound[legacyDepKey] === undefined) {
|
||||
outbound[legacyDepKey] = sourceValue;
|
||||
if (sourceValue === undefined) {
|
||||
continue;
|
||||
}
|
||||
for (const legacyDepKey of resolveLegacyDepKeysForChannel(channelId)) {
|
||||
if (outbound[legacyDepKey] === undefined) {
|
||||
outbound[legacyDepKey] = sourceValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { createPluginBoundaryRuntimeSend } from "./plugin-boundary-send.js";
|
||||
import { createChannelOutboundRuntimeSend } from "./channel-outbound-send.js";
|
||||
|
||||
export const runtimeSend = createPluginBoundaryRuntimeSend({
|
||||
pluginId: "discord",
|
||||
exportName: "sendMessageDiscord",
|
||||
missingLabel: "Discord plugin runtime",
|
||||
export const runtimeSend = createChannelOutboundRuntimeSend({
|
||||
channelId: "discord",
|
||||
unavailableMessage: "Discord outbound adapter is unavailable.",
|
||||
});
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
import { createCachedPluginBoundaryModuleLoader } from "../../plugins/runtime/runtime-plugin-boundary.js";
|
||||
|
||||
type RuntimeSendModule = Record<string, unknown>;
|
||||
|
||||
export type RuntimeSend = {
|
||||
sendMessage: (...args: unknown[]) => Promise<unknown>;
|
||||
};
|
||||
|
||||
function resolveRuntimeExport(
|
||||
module: RuntimeSendModule | null,
|
||||
pluginId: string,
|
||||
exportName: string,
|
||||
): (...args: unknown[]) => Promise<unknown> {
|
||||
const candidate = module?.[exportName];
|
||||
if (typeof candidate !== "function") {
|
||||
throw new Error(`${pluginId} plugin runtime is unavailable: missing export '${exportName}'`);
|
||||
}
|
||||
return candidate as (...args: unknown[]) => Promise<unknown>;
|
||||
}
|
||||
|
||||
export function createPluginBoundaryRuntimeSend(params: {
|
||||
pluginId: string;
|
||||
exportName: string;
|
||||
missingLabel: string;
|
||||
}): RuntimeSend {
|
||||
const loadRuntimeModuleSync = createCachedPluginBoundaryModuleLoader<RuntimeSendModule>({
|
||||
pluginId: params.pluginId,
|
||||
entryBaseName: "runtime-api",
|
||||
required: true,
|
||||
missingLabel: params.missingLabel,
|
||||
});
|
||||
|
||||
return {
|
||||
sendMessage: (...args) =>
|
||||
resolveRuntimeExport(loadRuntimeModuleSync(), params.pluginId, params.exportName)(...args),
|
||||
};
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
import { createPluginBoundaryRuntimeSend } from "./plugin-boundary-send.js";
|
||||
import { createChannelOutboundRuntimeSend } from "./channel-outbound-send.js";
|
||||
|
||||
export const runtimeSend = createPluginBoundaryRuntimeSend({
|
||||
pluginId: "signal",
|
||||
exportName: "sendMessageSignal",
|
||||
missingLabel: "Signal plugin runtime",
|
||||
export const runtimeSend = createChannelOutboundRuntimeSend({
|
||||
channelId: "signal",
|
||||
unavailableMessage: "Signal outbound adapter is unavailable.",
|
||||
});
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { createPluginBoundaryRuntimeSend } from "./plugin-boundary-send.js";
|
||||
import { createChannelOutboundRuntimeSend } from "./channel-outbound-send.js";
|
||||
|
||||
export const runtimeSend = createPluginBoundaryRuntimeSend({
|
||||
pluginId: "slack",
|
||||
exportName: "sendMessageSlack",
|
||||
missingLabel: "Slack plugin runtime",
|
||||
export const runtimeSend = createChannelOutboundRuntimeSend({
|
||||
channelId: "slack",
|
||||
unavailableMessage: "Slack outbound adapter is unavailable.",
|
||||
});
|
||||
|
||||
@@ -1,39 +1,6 @@
|
||||
import { loadChannelOutboundAdapter } from "../../channels/plugins/outbound/load.js";
|
||||
import { loadConfig } from "../../config/config.js";
|
||||
import { createChannelOutboundRuntimeSend } from "./channel-outbound-send.js";
|
||||
|
||||
type TelegramRuntimeSendOpts = {
|
||||
cfg?: ReturnType<typeof loadConfig>;
|
||||
mediaUrl?: string;
|
||||
mediaLocalRoots?: readonly string[];
|
||||
accountId?: string;
|
||||
messageThreadId?: string | number;
|
||||
replyToMessageId?: string | number;
|
||||
silent?: boolean;
|
||||
forceDocument?: boolean;
|
||||
gatewayClientScopes?: readonly string[];
|
||||
};
|
||||
|
||||
export const runtimeSend = {
|
||||
sendMessage: async (to: string, text: string, opts: TelegramRuntimeSendOpts = {}) => {
|
||||
const outbound = await loadChannelOutboundAdapter("telegram");
|
||||
if (!outbound?.sendText) {
|
||||
throw new Error("Telegram outbound adapter is unavailable.");
|
||||
}
|
||||
return await outbound.sendText({
|
||||
cfg: opts.cfg ?? loadConfig(),
|
||||
to,
|
||||
text,
|
||||
mediaUrl: opts.mediaUrl,
|
||||
mediaLocalRoots: opts.mediaLocalRoots,
|
||||
accountId: opts.accountId,
|
||||
threadId: opts.messageThreadId,
|
||||
replyToId:
|
||||
opts.replyToMessageId == null
|
||||
? undefined
|
||||
: String(opts.replyToMessageId).trim() || undefined,
|
||||
silent: opts.silent,
|
||||
forceDocument: opts.forceDocument,
|
||||
gatewayClientScopes: opts.gatewayClientScopes,
|
||||
});
|
||||
},
|
||||
};
|
||||
export const runtimeSend = createChannelOutboundRuntimeSend({
|
||||
channelId: "telegram",
|
||||
unavailableMessage: "Telegram outbound adapter is unavailable.",
|
||||
});
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { createPluginBoundaryRuntimeSend } from "./plugin-boundary-send.js";
|
||||
import { createChannelOutboundRuntimeSend } from "./channel-outbound-send.js";
|
||||
|
||||
export const runtimeSend = createPluginBoundaryRuntimeSend({
|
||||
pluginId: "whatsapp",
|
||||
exportName: "sendMessageWhatsApp",
|
||||
missingLabel: "WhatsApp plugin runtime",
|
||||
export const runtimeSend = createChannelOutboundRuntimeSend({
|
||||
channelId: "whatsapp",
|
||||
unavailableMessage: "WhatsApp outbound adapter is unavailable.",
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user