mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 02:31:22 +00:00
chore: merge origin/main into main
This commit is contained in:
@@ -4,6 +4,7 @@ import {
|
||||
createInboundDebouncer,
|
||||
resolveInboundDebounceMs,
|
||||
} from "../../auto-reply/inbound-debounce.js";
|
||||
import { resolveOpenProviderRuntimeGroupPolicy } from "../../config/runtime-group-policy.js";
|
||||
import { danger } from "../../globals.js";
|
||||
import type { DiscordMessageEvent, DiscordMessageHandler } from "./listeners.js";
|
||||
import { preflightDiscordMessage } from "./message-handler.preflight.js";
|
||||
@@ -23,7 +24,11 @@ type DiscordMessageHandlerParams = Omit<
|
||||
export function createDiscordMessageHandler(
|
||||
params: DiscordMessageHandlerParams,
|
||||
): DiscordMessageHandler {
|
||||
const groupPolicy = params.discordConfig?.groupPolicy ?? "open";
|
||||
const { groupPolicy } = resolveOpenProviderRuntimeGroupPolicy({
|
||||
providerConfigPresent: params.cfg.channels?.discord !== undefined,
|
||||
groupPolicy: params.discordConfig?.groupPolicy,
|
||||
defaultGroupPolicy: params.cfg.channels?.defaults?.groupPolicy,
|
||||
});
|
||||
const ackReactionScope = params.cfg.messages?.ackReactionScope ?? "group-mentions";
|
||||
const debounceMs = resolveInboundDebounceMs({ cfg: params.cfg, channel: "discord" });
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ import type { ReplyPayload } from "../../auto-reply/types.js";
|
||||
import { resolveCommandAuthorizedFromAuthorizers } from "../../channels/command-gating.js";
|
||||
import { createReplyPrefixOptions } from "../../channels/reply-prefix.js";
|
||||
import type { OpenClawConfig, loadConfig } from "../../config/config.js";
|
||||
import { resolveOpenProviderRuntimeGroupPolicy } from "../../config/runtime-group-policy.js";
|
||||
import { loadSessionStore, resolveStorePath } from "../../config/sessions.js";
|
||||
import { logVerbose } from "../../globals.js";
|
||||
import { createSubsystemLogger } from "../../logging/subsystem.js";
|
||||
@@ -1329,8 +1330,13 @@ async function dispatchDiscordCommandInteraction(params: {
|
||||
const channelAllowlistConfigured =
|
||||
Boolean(guildInfo?.channels) && Object.keys(guildInfo?.channels ?? {}).length > 0;
|
||||
const channelAllowed = channelConfig?.allowed !== false;
|
||||
const { groupPolicy } = resolveOpenProviderRuntimeGroupPolicy({
|
||||
providerConfigPresent: cfg.channels?.discord !== undefined,
|
||||
groupPolicy: discordConfig?.groupPolicy,
|
||||
defaultGroupPolicy: cfg.channels?.defaults?.groupPolicy,
|
||||
});
|
||||
const allowByPolicy = isDiscordGroupAllowedByPolicy({
|
||||
groupPolicy: discordConfig?.groupPolicy ?? "open",
|
||||
groupPolicy,
|
||||
guildAllowlisted: Boolean(guildInfo),
|
||||
channelAllowlistConfigured,
|
||||
channelAllowed,
|
||||
|
||||
38
src/discord/monitor/provider.group-policy.test.ts
Normal file
38
src/discord/monitor/provider.group-policy.test.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { __testing } from "./provider.js";
|
||||
|
||||
describe("resolveDiscordRuntimeGroupPolicy", () => {
|
||||
it("fails closed when channels.discord is missing and no defaults are set", () => {
|
||||
const resolved = __testing.resolveDiscordRuntimeGroupPolicy({
|
||||
providerConfigPresent: false,
|
||||
});
|
||||
expect(resolved.groupPolicy).toBe("allowlist");
|
||||
expect(resolved.providerMissingFallbackApplied).toBe(true);
|
||||
});
|
||||
|
||||
it("keeps open default when channels.discord is configured", () => {
|
||||
const resolved = __testing.resolveDiscordRuntimeGroupPolicy({
|
||||
providerConfigPresent: true,
|
||||
});
|
||||
expect(resolved.groupPolicy).toBe("open");
|
||||
expect(resolved.providerMissingFallbackApplied).toBe(false);
|
||||
});
|
||||
|
||||
it("respects explicit provider policy", () => {
|
||||
const resolved = __testing.resolveDiscordRuntimeGroupPolicy({
|
||||
providerConfigPresent: false,
|
||||
groupPolicy: "disabled",
|
||||
});
|
||||
expect(resolved.groupPolicy).toBe("disabled");
|
||||
expect(resolved.providerMissingFallbackApplied).toBe(false);
|
||||
});
|
||||
|
||||
it("ignores explicit global defaults when provider config is missing", () => {
|
||||
const resolved = __testing.resolveDiscordRuntimeGroupPolicy({
|
||||
providerConfigPresent: false,
|
||||
defaultGroupPolicy: "open",
|
||||
});
|
||||
expect(resolved.groupPolicy).toBe("allowlist");
|
||||
expect(resolved.providerMissingFallbackApplied).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -21,6 +21,12 @@ import {
|
||||
} from "../../config/commands.js";
|
||||
import type { OpenClawConfig, ReplyToMode } from "../../config/config.js";
|
||||
import { loadConfig } from "../../config/config.js";
|
||||
import {
|
||||
GROUP_POLICY_BLOCKED_LABEL,
|
||||
resolveOpenProviderRuntimeGroupPolicy,
|
||||
resolveDefaultGroupPolicy,
|
||||
warnMissingProviderGroupPolicyFallbackOnce,
|
||||
} from "../../config/runtime-group-policy.js";
|
||||
import { danger, logVerbose, shouldLogVerbose, warn } from "../../globals.js";
|
||||
import { formatErrorMessage } from "../../infra/errors.js";
|
||||
import { createDiscordRetryRunner } from "../../infra/retry-policy.js";
|
||||
@@ -245,27 +251,29 @@ export async function monitorDiscordProvider(opts: MonitorDiscordOpts = {}) {
|
||||
|
||||
const runtime: RuntimeEnv = opts.runtime ?? createNonExitingRuntime();
|
||||
|
||||
const discordCfg = account.config;
|
||||
const rawDiscordCfg = account.config;
|
||||
const discordRootThreadBindings = cfg.channels?.discord?.threadBindings;
|
||||
const discordAccountThreadBindings =
|
||||
cfg.channels?.discord?.accounts?.[account.accountId]?.threadBindings;
|
||||
const discordRestFetch = resolveDiscordRestFetch(discordCfg.proxy, runtime);
|
||||
const dmConfig = discordCfg.dm;
|
||||
let guildEntries = discordCfg.guilds;
|
||||
const defaultGroupPolicy = cfg.channels?.defaults?.groupPolicy;
|
||||
const groupPolicy = discordCfg.groupPolicy ?? defaultGroupPolicy ?? "open";
|
||||
if (
|
||||
discordCfg.groupPolicy === undefined &&
|
||||
discordCfg.guilds === undefined &&
|
||||
defaultGroupPolicy === undefined &&
|
||||
groupPolicy === "open"
|
||||
) {
|
||||
runtime.log?.(
|
||||
warn(
|
||||
'discord: groupPolicy defaults to "open" when channels.discord is missing; set channels.discord.groupPolicy (or channels.defaults.groupPolicy) or add channels.discord.guilds to restrict access.',
|
||||
),
|
||||
);
|
||||
}
|
||||
const discordRestFetch = resolveDiscordRestFetch(rawDiscordCfg.proxy, runtime);
|
||||
const dmConfig = rawDiscordCfg.dm;
|
||||
let guildEntries = rawDiscordCfg.guilds;
|
||||
const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg);
|
||||
const providerConfigPresent = cfg.channels?.discord !== undefined;
|
||||
const { groupPolicy, providerMissingFallbackApplied } = resolveOpenProviderRuntimeGroupPolicy({
|
||||
providerConfigPresent,
|
||||
groupPolicy: rawDiscordCfg.groupPolicy,
|
||||
defaultGroupPolicy,
|
||||
});
|
||||
const discordCfg =
|
||||
rawDiscordCfg.groupPolicy === groupPolicy ? rawDiscordCfg : { ...rawDiscordCfg, groupPolicy };
|
||||
warnMissingProviderGroupPolicyFallbackOnce({
|
||||
providerMissingFallbackApplied,
|
||||
providerKey: "discord",
|
||||
accountId: account.accountId,
|
||||
blockedLabel: GROUP_POLICY_BLOCKED_LABEL.guild,
|
||||
log: (message) => runtime.log?.(warn(message)),
|
||||
});
|
||||
let allowFrom = discordCfg.allowFrom ?? dmConfig?.allowFrom;
|
||||
const mediaMaxBytes = (opts.mediaMaxMb ?? discordCfg.mediaMaxMb ?? 8) * 1024 * 1024;
|
||||
const textLimit = resolveTextChunkLimit(cfg, "discord", account.accountId, {
|
||||
@@ -622,6 +630,8 @@ async function clearDiscordNativeCommands(params: {
|
||||
export const __testing = {
|
||||
createDiscordGatewayPlugin,
|
||||
dedupeSkillCommandsForDiscord,
|
||||
resolveDiscordRuntimeGroupPolicy: resolveOpenProviderRuntimeGroupPolicy,
|
||||
resolveDefaultGroupPolicy,
|
||||
resolveDiscordRestFetch,
|
||||
resolveThreadBindingsEnabled,
|
||||
};
|
||||
|
||||
@@ -62,6 +62,31 @@ type DiscordChannelMessageResult = {
|
||||
channel_id?: string | null;
|
||||
};
|
||||
|
||||
async function sendDiscordThreadTextChunks(params: {
|
||||
rest: RequestClient;
|
||||
threadId: string;
|
||||
chunks: readonly string[];
|
||||
request: DiscordClientRequest;
|
||||
maxLinesPerMessage?: number;
|
||||
chunkMode: ReturnType<typeof resolveChunkMode>;
|
||||
silent?: boolean;
|
||||
}): Promise<void> {
|
||||
for (const chunk of params.chunks) {
|
||||
await sendDiscordText(
|
||||
params.rest,
|
||||
params.threadId,
|
||||
chunk,
|
||||
undefined,
|
||||
params.request,
|
||||
params.maxLinesPerMessage,
|
||||
undefined,
|
||||
undefined,
|
||||
params.chunkMode,
|
||||
params.silent,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/** Discord thread names are capped at 100 characters. */
|
||||
const DISCORD_THREAD_NAME_LIMIT = 100;
|
||||
|
||||
@@ -194,35 +219,25 @@ export async function sendMessageDiscord(
|
||||
chunkMode,
|
||||
opts.silent,
|
||||
);
|
||||
for (const chunk of afterMediaChunks) {
|
||||
await sendDiscordText(
|
||||
rest,
|
||||
threadId,
|
||||
chunk,
|
||||
undefined,
|
||||
request,
|
||||
accountInfo.config.maxLinesPerMessage,
|
||||
undefined,
|
||||
undefined,
|
||||
chunkMode,
|
||||
opts.silent,
|
||||
);
|
||||
}
|
||||
await sendDiscordThreadTextChunks({
|
||||
rest,
|
||||
threadId,
|
||||
chunks: afterMediaChunks,
|
||||
request,
|
||||
maxLinesPerMessage: accountInfo.config.maxLinesPerMessage,
|
||||
chunkMode,
|
||||
silent: opts.silent,
|
||||
});
|
||||
} else {
|
||||
for (const chunk of remainingChunks) {
|
||||
await sendDiscordText(
|
||||
rest,
|
||||
threadId,
|
||||
chunk,
|
||||
undefined,
|
||||
request,
|
||||
accountInfo.config.maxLinesPerMessage,
|
||||
undefined,
|
||||
undefined,
|
||||
chunkMode,
|
||||
opts.silent,
|
||||
);
|
||||
}
|
||||
await sendDiscordThreadTextChunks({
|
||||
rest,
|
||||
threadId,
|
||||
chunks: remainingChunks,
|
||||
request,
|
||||
maxLinesPerMessage: accountInfo.config.maxLinesPerMessage,
|
||||
chunkMode,
|
||||
silent: opts.silent,
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
throw await buildDiscordSendError(err, {
|
||||
|
||||
Reference in New Issue
Block a user