mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-24 03:15:46 -06:00
refactor(discord): remove obsolete action runtime wrapper (#127123)
This commit is contained in:
committed by
GitHub
parent
be2f7c6a3d
commit
782a7d7aed
@@ -1,7 +1,7 @@
|
||||
// Discord tests cover channel actions.contract plugin behavior.
|
||||
import { installChannelActionsContractSuite } from "openclaw/plugin-sdk/channel-test-helpers";
|
||||
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
|
||||
import { describe } from "vitest";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { discordPlugin } from "../api.js";
|
||||
|
||||
describe("discord actions contract", () => {
|
||||
@@ -39,9 +39,13 @@ describe("discord actions contract", () => {
|
||||
},
|
||||
} as OpenClawConfig,
|
||||
expectedActions: ["send", "poll", "react", "reactions", "emoji-list"],
|
||||
expectedCanonicalOutboundActions: ["poll"],
|
||||
expectedCapabilities: ["presentation"],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
it("declines poll actions so canonical outbound delivery owns them", () => {
|
||||
expect(discordPlugin.actions?.supportsAction?.({ action: "poll" })).toBe(false);
|
||||
expect(discordPlugin.outbound?.sendPoll).toBeTypeOf("function");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -176,11 +176,13 @@ function describeDiscordMessageTool({
|
||||
}
|
||||
|
||||
export const discordMessageActions: ChannelMessageActionAdapter = {
|
||||
providerOwnedReadGates: true,
|
||||
// Credential-only Discord actions run in the gateway when one is available.
|
||||
// Send/file-style actions stay local because core owns their thread, media,
|
||||
// component, and client-local payload semantics.
|
||||
resolveExecutionMode: resolveDiscordActionExecutionMode,
|
||||
describeMessageTool: describeDiscordMessageTool,
|
||||
supportsAction: ({ action }) => action !== "poll",
|
||||
requiresTrustedRequesterSender: ({ action, toolContext }) =>
|
||||
Boolean(toolContext) && isTrustedRequesterGuildAdminAction(action),
|
||||
extractToolSend: ({ args }) => {
|
||||
|
||||
@@ -116,16 +116,12 @@ async function expectDiscordStartupDelay(
|
||||
}
|
||||
|
||||
function installDiscordRuntime(
|
||||
discord: Record<string, unknown>,
|
||||
openKeyedStore: (options: Record<string, unknown>) => unknown = vi.fn(() => ({
|
||||
lookup: vi.fn(async () => undefined),
|
||||
register: vi.fn(async () => undefined),
|
||||
})),
|
||||
) {
|
||||
setDiscordRuntime({
|
||||
channel: {
|
||||
discord,
|
||||
},
|
||||
logging: {
|
||||
shouldLogVerbose: () => false,
|
||||
},
|
||||
@@ -167,7 +163,7 @@ afterEach(() => {
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.useRealTimers();
|
||||
installDiscordRuntime({});
|
||||
installDiscordRuntime();
|
||||
});
|
||||
|
||||
beforeAll(async () => {
|
||||
@@ -453,12 +449,6 @@ describe("discordPlugin outbound", () => {
|
||||
});
|
||||
|
||||
it("uses direct Discord probe helpers for status probes", async () => {
|
||||
const runtimeProbeDiscord = vi.fn(async () => {
|
||||
throw new Error("runtime Discord probe should not be used");
|
||||
});
|
||||
installDiscordRuntime({
|
||||
probeDiscord: runtimeProbeDiscord,
|
||||
});
|
||||
probeDiscordMock.mockResolvedValue({
|
||||
ok: true,
|
||||
bot: { username: "Bob" },
|
||||
@@ -487,7 +477,6 @@ describe("discordPlugin outbound", () => {
|
||||
const forwardedTimeoutMs = Number(argAt(probeDiscordMock, 0, 1));
|
||||
expect(forwardedTimeoutMs).toBeGreaterThan(0);
|
||||
expect(forwardedTimeoutMs).toBeLessThanOrEqual(5_000);
|
||||
expect(runtimeProbeDiscord).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("subtracts lazy probe loading from the status budget", async () => {
|
||||
@@ -577,16 +566,6 @@ describe("discordPlugin outbound", () => {
|
||||
});
|
||||
|
||||
it("uses direct Discord startup helpers for async startup enrichment", async () => {
|
||||
const runtimeProbeDiscord = vi.fn(async () => {
|
||||
throw new Error("runtime Discord probe should not be used");
|
||||
});
|
||||
const runtimeMonitorDiscordProvider = vi.fn(async () => {
|
||||
throw new Error("runtime Discord monitor should not be used");
|
||||
});
|
||||
installDiscordRuntime({
|
||||
probeDiscord: runtimeProbeDiscord,
|
||||
monitorDiscordProvider: runtimeMonitorDiscordProvider,
|
||||
});
|
||||
probeDiscordMock.mockResolvedValue({
|
||||
ok: true,
|
||||
bot: { username: "Bob" },
|
||||
@@ -613,8 +592,6 @@ describe("discordPlugin outbound", () => {
|
||||
expect(monitorParams.token).toBe("discord-token");
|
||||
expect(monitorParams.accountId).toBe("default");
|
||||
expect(sleepWithAbortMock).not.toHaveBeenCalled();
|
||||
expect(runtimeProbeDiscord).not.toHaveBeenCalled();
|
||||
expect(runtimeMonitorDiscordProvider).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("fails loudly before provider startup when a token SecretRef is configured but unresolved", async () => {
|
||||
@@ -705,7 +682,7 @@ describe("discordPlugin outbound", () => {
|
||||
register: vi.fn(async () => undefined),
|
||||
};
|
||||
const openKeyedStore = vi.fn(() => commandDeployHashStore);
|
||||
installDiscordRuntime({}, openKeyedStore);
|
||||
installDiscordRuntime(openKeyedStore);
|
||||
|
||||
await startDiscordAccount(createCfg());
|
||||
|
||||
@@ -721,7 +698,7 @@ describe("discordPlugin outbound", () => {
|
||||
|
||||
it("continues Discord startup when the command deployment cache cannot open", async () => {
|
||||
prepareDiscordStartupMocks();
|
||||
installDiscordRuntime({}, () => {
|
||||
installDiscordRuntime(() => {
|
||||
throw new Error("SQLite unavailable");
|
||||
});
|
||||
|
||||
|
||||
@@ -4,10 +4,6 @@ import {
|
||||
createAccountScopedAllowlistNameResolver,
|
||||
createNestedAllowlistOverrideResolver,
|
||||
} from "openclaw/plugin-sdk/allowlist-config-edit";
|
||||
import type {
|
||||
ChannelMessageActionAdapter,
|
||||
ChannelMessageToolDiscovery,
|
||||
} from "openclaw/plugin-sdk/channel-contract";
|
||||
import { createChatChannelPlugin } from "openclaw/plugin-sdk/channel-core";
|
||||
import { createChannelMessageAdapterFromOutbound } from "openclaw/plugin-sdk/channel-outbound";
|
||||
import { createPairingPrefixStripper } from "openclaw/plugin-sdk/channel-pairing";
|
||||
@@ -31,7 +27,7 @@ import {
|
||||
} from "./accounts.js";
|
||||
import { getDiscordApprovalCapability } from "./approval-native.js";
|
||||
import { resolveRequiredDiscordChannelPermissions } from "./audit-core.js";
|
||||
import { discordMessageActions as discordMessageActionsImpl } from "./channel-actions.js";
|
||||
import { discordMessageActions } from "./channel-actions.js";
|
||||
import {
|
||||
buildTokenChannelStatusSummary,
|
||||
DEFAULT_ACCOUNT_ID,
|
||||
@@ -177,61 +173,6 @@ function shouldTreatDiscordDeliveredTextAsVisible(params: {
|
||||
);
|
||||
}
|
||||
|
||||
function resolveRuntimeDiscordMessageActions() {
|
||||
try {
|
||||
return getDiscordRuntime().channel?.discord?.messageActions ?? null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const discordMessageActions: ChannelMessageActionAdapter = {
|
||||
providerOwnedReadGates: true,
|
||||
resolveExecutionMode: (
|
||||
ctx: Parameters<NonNullable<ChannelMessageActionAdapter["resolveExecutionMode"]>>[0],
|
||||
) =>
|
||||
resolveRuntimeDiscordMessageActions()?.resolveExecutionMode?.(ctx) ??
|
||||
discordMessageActionsImpl.resolveExecutionMode?.(ctx) ??
|
||||
"local",
|
||||
describeMessageTool: (
|
||||
ctx: Parameters<NonNullable<ChannelMessageActionAdapter["describeMessageTool"]>>[0],
|
||||
): ChannelMessageToolDiscovery | null =>
|
||||
resolveRuntimeDiscordMessageActions()?.describeMessageTool?.(ctx) ??
|
||||
discordMessageActionsImpl.describeMessageTool?.(ctx) ??
|
||||
null,
|
||||
requiresTrustedRequesterSender: (
|
||||
ctx: Parameters<NonNullable<ChannelMessageActionAdapter["requiresTrustedRequesterSender"]>>[0],
|
||||
) =>
|
||||
resolveRuntimeDiscordMessageActions()?.requiresTrustedRequesterSender?.(ctx) ??
|
||||
discordMessageActionsImpl.requiresTrustedRequesterSender?.(ctx) ??
|
||||
false,
|
||||
extractToolSend: (
|
||||
ctx: Parameters<NonNullable<ChannelMessageActionAdapter["extractToolSend"]>>[0],
|
||||
) =>
|
||||
resolveRuntimeDiscordMessageActions()?.extractToolSend?.(ctx) ??
|
||||
discordMessageActionsImpl.extractToolSend?.(ctx) ??
|
||||
null,
|
||||
prepareSendPayload: (
|
||||
ctx: Parameters<NonNullable<ChannelMessageActionAdapter["prepareSendPayload"]>>[0],
|
||||
) =>
|
||||
resolveRuntimeDiscordMessageActions()?.prepareSendPayload?.(ctx) ??
|
||||
discordMessageActionsImpl.prepareSendPayload?.(ctx) ??
|
||||
null,
|
||||
supportsAction: ({ action }) => action !== "poll",
|
||||
handleAction: async (
|
||||
ctx: Parameters<NonNullable<ChannelMessageActionAdapter["handleAction"]>>[0],
|
||||
) => {
|
||||
const runtimeHandleAction = resolveRuntimeDiscordMessageActions()?.handleAction;
|
||||
if (runtimeHandleAction) {
|
||||
return await runtimeHandleAction(ctx);
|
||||
}
|
||||
if (!discordMessageActionsImpl.handleAction) {
|
||||
throw new Error("Discord message actions not available");
|
||||
}
|
||||
return await discordMessageActionsImpl.handleAction(ctx);
|
||||
},
|
||||
};
|
||||
|
||||
function resolveDiscordStartupDelayMs(cfg: OpenClawConfig, accountId: string): number {
|
||||
const startupAccountIds = listDiscordStartupAccountIds(cfg);
|
||||
const startupIndex = startupAccountIds.findIndex((candidateId) => candidateId === accountId);
|
||||
|
||||
@@ -2,22 +2,11 @@
|
||||
import type { PluginRuntime } from "openclaw/plugin-sdk/channel-core";
|
||||
import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
|
||||
|
||||
type DiscordChannelRuntime = {
|
||||
messageActions?: typeof import("./channel-actions.js").discordMessageActions;
|
||||
sendMessageDiscord?: typeof import("./send.js").sendMessageDiscord;
|
||||
};
|
||||
|
||||
type DiscordRuntime = PluginRuntime & {
|
||||
channel: PluginRuntime["channel"] & {
|
||||
discord?: DiscordChannelRuntime;
|
||||
};
|
||||
};
|
||||
|
||||
const {
|
||||
setRuntime: setDiscordRuntime,
|
||||
tryGetRuntime: getOptionalDiscordRuntime,
|
||||
getRuntime: getDiscordRuntime,
|
||||
} = createPluginRuntimeStore<DiscordRuntime>({
|
||||
} = createPluginRuntimeStore<PluginRuntime>({
|
||||
pluginId: "discord",
|
||||
errorMessage: "Discord runtime not initialized",
|
||||
});
|
||||
|
||||
@@ -66,23 +66,12 @@ type ChannelActionsContractCase = {
|
||||
name: string;
|
||||
cfg: OpenClawConfig;
|
||||
expectedActions: readonly ChannelMessageActionName[];
|
||||
expectedCanonicalOutboundActions?: readonly ChannelMessageActionName[];
|
||||
expectedCapabilities?: readonly ChannelMessageCapability[];
|
||||
beforeTest?: () => void;
|
||||
};
|
||||
|
||||
function hasCanonicalOutboundAction(
|
||||
plugin: Pick<ChannelPlugin, "outbound">,
|
||||
action: ChannelMessageActionName,
|
||||
) {
|
||||
if (action !== "poll") {
|
||||
return false;
|
||||
}
|
||||
return Boolean(plugin.outbound?.sendPoll);
|
||||
}
|
||||
|
||||
export function installChannelActionsContractSuite(params: {
|
||||
plugin: Pick<ChannelPlugin, "id" | "actions" | "outbound">;
|
||||
plugin: Pick<ChannelPlugin, "id" | "actions">;
|
||||
cases: readonly ChannelActionsContractCase[];
|
||||
unsupportedAction?: ChannelMessageActionName;
|
||||
}) {
|
||||
@@ -107,29 +96,14 @@ export function installChannelActionsContractSuite(params: {
|
||||
expect(sortStrings(actions)).toEqual(sortStrings(testCase.expectedActions));
|
||||
expect(sortStrings(capabilities)).toEqual(sortStrings(testCase.expectedCapabilities ?? []));
|
||||
|
||||
const canonicalOutboundActions = new Set(testCase.expectedCanonicalOutboundActions ?? []);
|
||||
for (const action of canonicalOutboundActions) {
|
||||
expect(actions).toContain(action);
|
||||
expect(hasCanonicalOutboundAction(params.plugin, action)).toBe(true);
|
||||
expect(params.plugin.actions?.supportsAction).toBeTypeOf("function");
|
||||
expect(params.plugin.actions?.supportsAction?.({ action })).toBe(false);
|
||||
}
|
||||
|
||||
if (params.plugin.actions?.supportsAction) {
|
||||
for (const action of testCase.expectedActions) {
|
||||
if (canonicalOutboundActions.has(action)) {
|
||||
continue;
|
||||
}
|
||||
expect(params.plugin.actions.supportsAction({ action })).toBe(true);
|
||||
}
|
||||
if (
|
||||
params.unsupportedAction &&
|
||||
!testCase.expectedActions.includes(params.unsupportedAction)
|
||||
) {
|
||||
expect(params.plugin.actions.supportsAction({ action: params.unsupportedAction })).toBe(
|
||||
false,
|
||||
);
|
||||
}
|
||||
if (
|
||||
params.plugin.actions?.supportsAction &&
|
||||
params.unsupportedAction &&
|
||||
!testCase.expectedActions.includes(params.unsupportedAction)
|
||||
) {
|
||||
expect(params.plugin.actions.supportsAction({ action: params.unsupportedAction })).toBe(
|
||||
false,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user