mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 09:00:42 +00:00
fix(discord): skip disabled reaction fetches
This commit is contained in:
@@ -38,6 +38,7 @@ Docs: https://docs.openclaw.ai
|
||||
- CLI/status: show skipped fast-path memory checks as `not checked` and report active custom memory plugin runtime status from `status --json --all` without requiring built-in `agents.defaults.memorySearch`, so plugins such as memory-lancedb-pro and memory-cms no longer look unavailable when their own runtime is healthy. Fixes #56968. Thanks @Tony-ooo and @aderius.
|
||||
- Gateway/channels: record and log unexpected clean channel monitor exits so channels that return without throwing no longer appear stopped with no error. Fixes #73099. Thanks @balaji1968-kingler.
|
||||
- Plugins/package: force nested bundled-plugin runtime dependency installs out of inherited npm dry-run mode during prepack and package smoke checks, so packed installs materialize required plugin modules instead of reporting missing bundled files. Refs #73128. Thanks @Adam-Researchh.
|
||||
- Discord: skip reaction events before REST channel fetch when notifications are off, guild reactions are disabled, or allowlist mode cannot match without channel overrides, reducing reconnect bursts that caused slow listener warnings. Fixes #73133. Thanks @isaacsummers.
|
||||
- Channels/Telegram: centralize polling update tracking so accepted offsets remain durable across restarts, same-process handler failures can still retry, and slow offset writes cannot overwrite newer accepted watermarks. Refs #73115. Thanks @vdruts.
|
||||
- Agents/models: classify empty, reasoning-only, and planning-only terminal agent runs before accepting a model fallback candidate, so invalid or incompatible models can advance to the next configured fallback instead of returning a 30-second terminal failure. Fixes #73115. Thanks @vdruts.
|
||||
- Memory/LanceDB: let embedding config use provider-backed auth profiles, environment credentials, or provider config without a separate plugin `embedding.apiKey`, so OAuth-capable embedding providers can power auto-recall/capture. Fixes #68950. Thanks @malshaalan-ai.
|
||||
|
||||
@@ -985,6 +985,10 @@ function makeReactionClient(options?: {
|
||||
} as unknown as DiscordReactionClient;
|
||||
}
|
||||
|
||||
function getReactionClientFetchChannelMock(client: DiscordReactionClient) {
|
||||
return (client as unknown as { fetchChannel: ReturnType<typeof vi.fn> }).fetchChannel;
|
||||
}
|
||||
|
||||
function makeReactionListenerParams(overrides?: {
|
||||
botUserId?: string;
|
||||
dmEnabled?: boolean;
|
||||
@@ -1127,6 +1131,7 @@ describe("discord DM reaction handling", () => {
|
||||
|
||||
await listener.handle(data, client);
|
||||
|
||||
expect(getReactionClientFetchChannelMock(client)).not.toHaveBeenCalled();
|
||||
expect(enqueueSystemEventSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -1191,6 +1196,7 @@ describe("discord DM reaction handling", () => {
|
||||
|
||||
await listener.handle(data, client);
|
||||
|
||||
expect(getReactionClientFetchChannelMock(client)).toHaveBeenCalled();
|
||||
expect(enqueueSystemEventSpy).toHaveBeenCalledOnce();
|
||||
const [text] = enqueueSystemEventSpy.mock.calls[0];
|
||||
expect(text).toContain("Discord reaction added");
|
||||
@@ -1251,6 +1257,7 @@ describe("discord reaction notification modes", () => {
|
||||
channelId: string | undefined;
|
||||
parentId: string | undefined;
|
||||
messageAuthorId: string;
|
||||
expectedFetchChannelCalls: number;
|
||||
expectedMessageFetchCalls: number;
|
||||
expectedEnqueueCalls: number;
|
||||
}>([
|
||||
@@ -1263,6 +1270,7 @@ describe("discord reaction notification modes", () => {
|
||||
channelId: undefined,
|
||||
parentId: undefined,
|
||||
messageAuthorId: "other-user",
|
||||
expectedFetchChannelCalls: 0,
|
||||
expectedMessageFetchCalls: 0,
|
||||
expectedEnqueueCalls: 0,
|
||||
},
|
||||
@@ -1275,6 +1283,7 @@ describe("discord reaction notification modes", () => {
|
||||
channelId: undefined,
|
||||
parentId: undefined,
|
||||
messageAuthorId: "other-user",
|
||||
expectedFetchChannelCalls: 1,
|
||||
expectedMessageFetchCalls: 0,
|
||||
expectedEnqueueCalls: 1,
|
||||
},
|
||||
@@ -1287,9 +1296,23 @@ describe("discord reaction notification modes", () => {
|
||||
channelId: undefined,
|
||||
parentId: undefined,
|
||||
messageAuthorId: "other-user",
|
||||
expectedFetchChannelCalls: 1,
|
||||
expectedMessageFetchCalls: 0,
|
||||
expectedEnqueueCalls: 1,
|
||||
},
|
||||
{
|
||||
name: "allowlist mode denied without channel overrides",
|
||||
reactionNotifications: "allowlist" as const,
|
||||
users: ["trusted-user"] as string[],
|
||||
userId: "untrusted-user",
|
||||
channelType: ChannelType.GuildText,
|
||||
channelId: undefined,
|
||||
parentId: undefined,
|
||||
messageAuthorId: "other-user",
|
||||
expectedFetchChannelCalls: 0,
|
||||
expectedMessageFetchCalls: 0,
|
||||
expectedEnqueueCalls: 0,
|
||||
},
|
||||
{
|
||||
name: "own mode",
|
||||
reactionNotifications: "own" as const,
|
||||
@@ -1299,6 +1322,7 @@ describe("discord reaction notification modes", () => {
|
||||
channelId: undefined,
|
||||
parentId: undefined,
|
||||
messageAuthorId: "bot-1",
|
||||
expectedFetchChannelCalls: 1,
|
||||
expectedMessageFetchCalls: 1,
|
||||
expectedEnqueueCalls: 1,
|
||||
},
|
||||
@@ -1311,6 +1335,7 @@ describe("discord reaction notification modes", () => {
|
||||
channelId: "thread-1",
|
||||
parentId: "parent-1",
|
||||
messageAuthorId: "other-user",
|
||||
expectedFetchChannelCalls: 2,
|
||||
expectedMessageFetchCalls: 0,
|
||||
expectedEnqueueCalls: 1,
|
||||
},
|
||||
@@ -1344,6 +1369,9 @@ describe("discord reaction notification modes", () => {
|
||||
|
||||
await listener.handle(data, client);
|
||||
|
||||
expect(getReactionClientFetchChannelMock(client), testCase.name).toHaveBeenCalledTimes(
|
||||
testCase.expectedFetchChannelCalls,
|
||||
);
|
||||
expect(messageFetch, testCase.name).toHaveBeenCalledTimes(testCase.expectedMessageFetchCalls);
|
||||
expect(enqueueSystemEventSpy, testCase.name).toHaveBeenCalledTimes(
|
||||
testCase.expectedEnqueueCalls,
|
||||
|
||||
@@ -521,6 +521,42 @@ async function handleDiscordChannelReactionNotification(params: {
|
||||
params.emitReactionWithAuthor(message);
|
||||
}
|
||||
|
||||
function hasDiscordGuildChannelOverrides(
|
||||
guildInfo: import("./allow-list.js").DiscordGuildEntryResolved | null,
|
||||
) {
|
||||
return Boolean(guildInfo?.channels && Object.keys(guildInfo.channels).length > 0);
|
||||
}
|
||||
|
||||
function shouldSkipGuildReactionBeforeChannelFetch(params: {
|
||||
reactionMode: DiscordReactionMode;
|
||||
guildInfo: import("./allow-list.js").DiscordGuildEntryResolved | null;
|
||||
groupPolicy: DiscordReactionRoutingParams["groupPolicy"];
|
||||
memberRoleIds: string[];
|
||||
user: User;
|
||||
botUserId?: string;
|
||||
allowNameMatching: boolean;
|
||||
}) {
|
||||
if (params.reactionMode === "off" || params.groupPolicy === "disabled") {
|
||||
return true;
|
||||
}
|
||||
if (params.reactionMode !== "allowlist") {
|
||||
return false;
|
||||
}
|
||||
if (hasDiscordGuildChannelOverrides(params.guildInfo)) {
|
||||
return false;
|
||||
}
|
||||
return !shouldEmitDiscordReactionNotification({
|
||||
mode: params.reactionMode,
|
||||
botId: params.botUserId,
|
||||
userId: params.user.id,
|
||||
userName: params.user.username,
|
||||
userTag: formatDiscordUserTag(params.user),
|
||||
guildInfo: params.guildInfo,
|
||||
memberRoleIds: params.memberRoleIds,
|
||||
allowNameMatching: params.allowNameMatching,
|
||||
});
|
||||
}
|
||||
|
||||
async function handleDiscordReactionEvent(
|
||||
params: {
|
||||
data: DiscordReactionEvent;
|
||||
@@ -556,6 +592,24 @@ async function handleDiscordReactionEvent(
|
||||
if (isGuildMessage && guildEntries && Object.keys(guildEntries).length > 0 && !guildInfo) {
|
||||
return;
|
||||
}
|
||||
const memberRoleIds = Array.isArray(data.rawMember?.roles)
|
||||
? data.rawMember.roles.map((roleId: string) => roleId)
|
||||
: [];
|
||||
const reactionMode = guildInfo?.reactionNotifications ?? "own";
|
||||
if (
|
||||
isGuildMessage &&
|
||||
shouldSkipGuildReactionBeforeChannelFetch({
|
||||
reactionMode,
|
||||
guildInfo,
|
||||
groupPolicy: params.groupPolicy,
|
||||
memberRoleIds,
|
||||
user,
|
||||
botUserId,
|
||||
allowNameMatching: params.allowNameMatching,
|
||||
})
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const channel = await client.fetchChannel(data.channel_id);
|
||||
if (!channel) {
|
||||
@@ -572,9 +626,6 @@ async function handleDiscordReactionEvent(
|
||||
const isDirectMessage = channelType === ChannelType.DM;
|
||||
const isGroupDm = channelType === ChannelType.GroupDM;
|
||||
const isThreadChannel = channelContext.isThreadChannel;
|
||||
const memberRoleIds = Array.isArray(data.rawMember?.roles)
|
||||
? data.rawMember.roles.map((roleId: string) => roleId)
|
||||
: [];
|
||||
const reactionIngressBase: Omit<DiscordReactionIngressAuthorizationParams, "channelConfig"> = {
|
||||
accountId: params.accountId,
|
||||
user,
|
||||
@@ -695,7 +746,6 @@ async function handleDiscordReactionEvent(
|
||||
};
|
||||
|
||||
if (isThreadChannel) {
|
||||
const reactionMode = guildInfo?.reactionNotifications ?? "own";
|
||||
await handleDiscordThreadReactionNotification({
|
||||
reactionMode,
|
||||
message: data.message,
|
||||
@@ -720,7 +770,6 @@ async function handleDiscordReactionEvent(
|
||||
parentSlug,
|
||||
scope: "channel",
|
||||
});
|
||||
const reactionMode = guildInfo?.reactionNotifications ?? "own";
|
||||
await handleDiscordChannelReactionNotification({
|
||||
isGuildMessage,
|
||||
reactionMode,
|
||||
|
||||
Reference in New Issue
Block a user