mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 07:30:43 +00:00
fix(discord): keep degraded DMs on direct routes
This commit is contained in:
@@ -35,6 +35,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Channels/status reactions: remove stale non-terminal lifecycle reactions when a run reaches done or error, so Discord does not leave a permanent thinking emoji after completion. Fixes #75458. Thanks @davelutztx.
|
||||
- Discord/doctor: migrate unsupported per-channel `agentId` entries under guild channel config into top-level `bindings[]` routes, so `openclaw doctor --fix` preserves the intended agent route instead of stripping it as an unknown key. Fixes #62455. Thanks @lobster-biscuit.
|
||||
- Discord/DMs: set inbound direct-message `ctx.To` to the semantic `user:<id>` target while keeping delivery routed through the DM channel, so mirror and recovery paths do not treat DMs as channel conversations. Fixes #68126. Thanks @illuminate0623.
|
||||
- Discord/DMs: keep no-guild inbound messages on direct-message routing when Discord channel lookup is temporarily unavailable, preventing degraded DMs from forking into channel sessions. Fixes #59817. Thanks @DooPeePey.
|
||||
- Gateway/config: log config health-state write failures instead of silently hiding config observe-recovery write errors. Thanks @sallyom.
|
||||
- Diagnostics: reset stuck-session timers on reply, tool, status, block, and ACP progress events, and back off repeated `session.stuck` diagnostics while a session remains unchanged. Supersedes #72010. Thanks @rubencu.
|
||||
|
||||
|
||||
@@ -117,6 +117,12 @@ function createDmClient(channelId: string): DiscordClient {
|
||||
} as unknown as DiscordClient;
|
||||
}
|
||||
|
||||
function createMissingChannelClient(): DiscordClient {
|
||||
return {
|
||||
fetchChannel: async () => null,
|
||||
} as unknown as DiscordClient;
|
||||
}
|
||||
|
||||
async function runThreadBoundPreflight(params: {
|
||||
threadId: string;
|
||||
parentId: string;
|
||||
@@ -203,6 +209,26 @@ async function runDmPreflight(params: {
|
||||
});
|
||||
}
|
||||
|
||||
async function runUnresolvedDmPreflight(params: {
|
||||
cfg?: import("openclaw/plugin-sdk/config-types").OpenClawConfig;
|
||||
channelId: string;
|
||||
message: import("../internal/discord.js").Message;
|
||||
discordConfig: DiscordConfig;
|
||||
}) {
|
||||
return preflightDiscordMessage({
|
||||
...createPreflightArgs({
|
||||
cfg: params.cfg ?? DEFAULT_PREFLIGHT_CFG,
|
||||
discordConfig: params.discordConfig,
|
||||
data: {
|
||||
channel_id: params.channelId,
|
||||
author: params.message.author,
|
||||
message: params.message,
|
||||
} as DiscordMessageEvent,
|
||||
client: createMissingChannelClient(),
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
async function runMentionOnlyBotPreflight(params: {
|
||||
channelId: string;
|
||||
guildId: string;
|
||||
@@ -483,6 +509,38 @@ describe("preflightDiscordMessage", () => {
|
||||
expect(result?.preflightAudioTranscript).toBe("hello openclaw from dm audio");
|
||||
});
|
||||
|
||||
it("keeps no-guild messages direct when channel lookup is unavailable", async () => {
|
||||
const result = await runUnresolvedDmPreflight({
|
||||
cfg: {
|
||||
...DEFAULT_PREFLIGHT_CFG,
|
||||
session: {
|
||||
...DEFAULT_PREFLIGHT_CFG.session,
|
||||
dmScope: "per-channel-peer",
|
||||
},
|
||||
},
|
||||
channelId: "dm-channel-unresolved-1",
|
||||
message: createDiscordMessage({
|
||||
id: "m-dm-unresolved-1",
|
||||
channelId: "dm-channel-unresolved-1",
|
||||
content: "hello from a degraded dm",
|
||||
author: {
|
||||
id: "user-1",
|
||||
bot: false,
|
||||
username: "alice",
|
||||
},
|
||||
}),
|
||||
discordConfig: {
|
||||
dmPolicy: "open",
|
||||
} as DiscordConfig,
|
||||
});
|
||||
|
||||
expect(result).not.toBeNull();
|
||||
expect(result?.channelInfo).toBeNull();
|
||||
expect(result?.isDirectMessage).toBe(true);
|
||||
expect(result?.isGroupDm).toBe(false);
|
||||
expect(result?.route.sessionKey).toBe("agent:main:discord:direct:user-1");
|
||||
});
|
||||
|
||||
it("falls back to the default discord account for omitted-account dm authorization", async () => {
|
||||
const message = createDiscordMessage({
|
||||
id: "m-dm-default-account",
|
||||
|
||||
@@ -70,6 +70,17 @@ export {
|
||||
shouldIgnoreBoundThreadWebhookMessage,
|
||||
} from "./message-handler.preflight-helpers.js";
|
||||
|
||||
function resolveDiscordPreflightConversationKind(params: {
|
||||
isGuildMessage: boolean;
|
||||
channelType?: ChannelType;
|
||||
}) {
|
||||
const isGroupDm = params.channelType === ChannelType.GroupDM;
|
||||
const isDirectMessage =
|
||||
params.channelType === ChannelType.DM ||
|
||||
(!params.isGuildMessage && !isGroupDm && params.channelType == null);
|
||||
return { isDirectMessage, isGroupDm };
|
||||
}
|
||||
|
||||
export async function preflightDiscordMessage(
|
||||
params: DiscordMessagePreflightParams,
|
||||
): Promise<DiscordMessagePreflightContext | null> {
|
||||
@@ -137,8 +148,10 @@ export async function preflightDiscordMessage(
|
||||
if (isPreflightAborted(params.abortSignal)) {
|
||||
return null;
|
||||
}
|
||||
const isDirectMessage = channelInfo?.type === ChannelType.DM;
|
||||
const isGroupDm = channelInfo?.type === ChannelType.GroupDM;
|
||||
const { isDirectMessage, isGroupDm } = resolveDiscordPreflightConversationKind({
|
||||
isGuildMessage,
|
||||
channelType: channelInfo?.type,
|
||||
});
|
||||
const messageText = resolveDiscordMessageText(message, {
|
||||
includeForwarded: true,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user