mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 06:50:43 +00:00
fix(discord): skip disabled reaction listeners
This commit is contained in:
@@ -21,6 +21,7 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
### Fixes
|
||||
|
||||
- Discord/reactions: skip reaction listener registration when DMs and group DMs are disabled and every configured guild has `reactionNotifications: "off"`, avoiding needless reaction-event queue work. Fixes #47516. Thanks @x4v13r1120.
|
||||
- Telegram/streaming: keep partial preview streaming enabled for plain reply-to replies, disabling drafts only for real native quote excerpts that require Telegram quote parameters. Fixes #73505. Thanks @choury.
|
||||
- Config: log the "newer OpenClaw" version warning once per process instead of once per config snapshot read. (#75927) Thanks @romneyda.
|
||||
- Telegram/message actions: treat benign delete-message 400s as no-op warnings instead of runtime errors, so stale or already-removed messages do not create noisy delete failures. Fixes #73726. Thanks @Avicennasis.
|
||||
|
||||
@@ -67,12 +67,24 @@ vi.mock("./gateway-supervisor.js", () => ({
|
||||
}));
|
||||
|
||||
vi.mock("./listeners.js", () => ({
|
||||
DiscordMessageListener: function DiscordMessageListener() {},
|
||||
DiscordInteractionListener: function DiscordInteractionListener() {},
|
||||
DiscordPresenceListener: function DiscordPresenceListener() {},
|
||||
DiscordReactionListener: function DiscordReactionListener() {},
|
||||
DiscordReactionRemoveListener: function DiscordReactionRemoveListener() {},
|
||||
DiscordThreadUpdateListener: function DiscordThreadUpdateListener() {},
|
||||
DiscordMessageListener: function DiscordMessageListener() {
|
||||
return { type: "message" };
|
||||
},
|
||||
DiscordInteractionListener: function DiscordInteractionListener() {
|
||||
return { type: "interaction" };
|
||||
},
|
||||
DiscordPresenceListener: function DiscordPresenceListener() {
|
||||
return { type: "presence" };
|
||||
},
|
||||
DiscordReactionListener: function DiscordReactionListener() {
|
||||
return { type: "reaction-add" };
|
||||
},
|
||||
DiscordReactionRemoveListener: function DiscordReactionRemoveListener() {
|
||||
return { type: "reaction-remove" };
|
||||
},
|
||||
DiscordThreadUpdateListener: function DiscordThreadUpdateListener() {
|
||||
return { type: "thread-update" };
|
||||
},
|
||||
registerDiscordListener: vi.fn(),
|
||||
}));
|
||||
|
||||
@@ -81,13 +93,19 @@ vi.mock("./presence.js", () => ({
|
||||
}));
|
||||
|
||||
import { createDiscordRequestClient, DISCORD_REST_TIMEOUT_MS } from "../proxy-request-client.js";
|
||||
import { createDiscordMonitorClient, fetchDiscordBotIdentity } from "./provider.startup.js";
|
||||
import { registerDiscordListener } from "./listeners.js";
|
||||
import {
|
||||
createDiscordMonitorClient,
|
||||
fetchDiscordBotIdentity,
|
||||
registerDiscordMonitorListeners,
|
||||
} from "./provider.startup.js";
|
||||
|
||||
describe("createDiscordMonitorClient", () => {
|
||||
beforeEach(() => {
|
||||
registerVoiceClientSpy.mockReset();
|
||||
waitForDiscordGatewayPluginRegistrationMock.mockReset().mockReturnValue(undefined);
|
||||
vi.mocked(createDiscordRequestClient).mockClear();
|
||||
vi.mocked(registerDiscordListener).mockClear();
|
||||
});
|
||||
|
||||
function createRuntime() {
|
||||
@@ -296,6 +314,92 @@ describe("createDiscordMonitorClient", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("registerDiscordMonitorListeners", () => {
|
||||
beforeEach(() => {
|
||||
vi.mocked(registerDiscordListener).mockClear();
|
||||
});
|
||||
|
||||
function createRuntime() {
|
||||
return {
|
||||
log: vi.fn(),
|
||||
error: vi.fn(),
|
||||
exit: vi.fn(),
|
||||
};
|
||||
}
|
||||
|
||||
function createListenerParams(
|
||||
overrides: Partial<Parameters<typeof registerDiscordMonitorListeners>[0]> = {},
|
||||
): Parameters<typeof registerDiscordMonitorListeners>[0] {
|
||||
return {
|
||||
cfg: {},
|
||||
client: { listeners: [] },
|
||||
accountId: "default",
|
||||
discordConfig: {},
|
||||
runtime: createRuntime(),
|
||||
botUserId: "bot-1",
|
||||
dmEnabled: false,
|
||||
groupDmEnabled: false,
|
||||
groupDmChannels: [],
|
||||
dmPolicy: "disabled",
|
||||
allowFrom: [],
|
||||
groupPolicy: "allowlist",
|
||||
guildEntries: {
|
||||
"guild-1": {
|
||||
id: "guild-1",
|
||||
reactionNotifications: "off",
|
||||
},
|
||||
},
|
||||
logger: {},
|
||||
messageHandler: {},
|
||||
...overrides,
|
||||
} as Parameters<typeof registerDiscordMonitorListeners>[0];
|
||||
}
|
||||
|
||||
function registeredListenerTypes() {
|
||||
return vi.mocked(registerDiscordListener).mock.calls.map((call) => {
|
||||
const listener = call[1] as { type?: string };
|
||||
return listener.type;
|
||||
});
|
||||
}
|
||||
|
||||
it("skips reaction listeners when every configured guild disables reactions and DMs are off", () => {
|
||||
registerDiscordMonitorListeners(createListenerParams());
|
||||
|
||||
expect(registeredListenerTypes()).toEqual(["interaction", "message", "thread-update"]);
|
||||
});
|
||||
|
||||
it("keeps reaction listeners when direct messages can emit reaction notifications", () => {
|
||||
registerDiscordMonitorListeners(
|
||||
createListenerParams({
|
||||
dmEnabled: true,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(registeredListenerTypes()).toContain("reaction-add");
|
||||
expect(registeredListenerTypes()).toContain("reaction-remove");
|
||||
});
|
||||
|
||||
it("keeps reaction listeners when a configured guild enables reaction notifications", () => {
|
||||
registerDiscordMonitorListeners(
|
||||
createListenerParams({
|
||||
guildEntries: {
|
||||
"guild-1": {
|
||||
id: "guild-1",
|
||||
reactionNotifications: "off",
|
||||
},
|
||||
"guild-2": {
|
||||
id: "guild-2",
|
||||
reactionNotifications: "own",
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
expect(registeredListenerTypes()).toContain("reaction-add");
|
||||
expect(registeredListenerTypes()).toContain("reaction-remove");
|
||||
});
|
||||
});
|
||||
|
||||
describe("fetchDiscordBotIdentity", () => {
|
||||
it("derives the bot id from a Discord bot token without calling /users/@me", async () => {
|
||||
const fetchUser = vi.fn(async () => {
|
||||
|
||||
@@ -263,30 +263,32 @@ export function registerDiscordMonitorListeners(params: {
|
||||
new DiscordMessageListener(params.messageHandler, params.logger, params.trackInboundEvent),
|
||||
);
|
||||
|
||||
const reactionListenerOptions: ConstructorParameters<typeof DiscordReactionListener>[0] = {
|
||||
cfg: params.cfg,
|
||||
accountId: params.accountId,
|
||||
runtime: params.runtime,
|
||||
botUserId: params.botUserId,
|
||||
dmEnabled: params.dmEnabled,
|
||||
groupDmEnabled: params.groupDmEnabled,
|
||||
groupDmChannels: params.groupDmChannels ?? [],
|
||||
dmPolicy: params.dmPolicy,
|
||||
allowFrom: params.allowFrom ?? [],
|
||||
groupPolicy: params.groupPolicy,
|
||||
allowNameMatching: isDangerousNameMatchingEnabled(params.discordConfig),
|
||||
guildEntries: params.guildEntries,
|
||||
logger: params.logger,
|
||||
onEvent: params.trackInboundEvent,
|
||||
};
|
||||
registerDiscordListener(
|
||||
params.client.listeners,
|
||||
new DiscordReactionListener(reactionListenerOptions),
|
||||
);
|
||||
registerDiscordListener(
|
||||
params.client.listeners,
|
||||
new DiscordReactionRemoveListener(reactionListenerOptions),
|
||||
);
|
||||
if (shouldRegisterDiscordReactionListeners(params)) {
|
||||
const reactionListenerOptions: ConstructorParameters<typeof DiscordReactionListener>[0] = {
|
||||
cfg: params.cfg,
|
||||
accountId: params.accountId,
|
||||
runtime: params.runtime,
|
||||
botUserId: params.botUserId,
|
||||
dmEnabled: params.dmEnabled,
|
||||
groupDmEnabled: params.groupDmEnabled,
|
||||
groupDmChannels: params.groupDmChannels ?? [],
|
||||
dmPolicy: params.dmPolicy,
|
||||
allowFrom: params.allowFrom ?? [],
|
||||
groupPolicy: params.groupPolicy,
|
||||
allowNameMatching: isDangerousNameMatchingEnabled(params.discordConfig),
|
||||
guildEntries: params.guildEntries,
|
||||
logger: params.logger,
|
||||
onEvent: params.trackInboundEvent,
|
||||
};
|
||||
registerDiscordListener(
|
||||
params.client.listeners,
|
||||
new DiscordReactionListener(reactionListenerOptions),
|
||||
);
|
||||
registerDiscordListener(
|
||||
params.client.listeners,
|
||||
new DiscordReactionRemoveListener(reactionListenerOptions),
|
||||
);
|
||||
}
|
||||
registerDiscordListener(
|
||||
params.client.listeners,
|
||||
new DiscordThreadUpdateListener(params.cfg, params.accountId, params.logger),
|
||||
@@ -300,3 +302,22 @@ export function registerDiscordMonitorListeners(params: {
|
||||
params.runtime.log?.("discord: GuildPresences intent enabled — presence listener registered");
|
||||
}
|
||||
}
|
||||
|
||||
function shouldRegisterDiscordReactionListeners(params: {
|
||||
dmEnabled: boolean;
|
||||
groupDmEnabled: boolean;
|
||||
groupPolicy: "open" | "allowlist" | "disabled";
|
||||
guildEntries?: Record<string, DiscordGuildEntryResolved>;
|
||||
}): boolean {
|
||||
if (params.dmEnabled || params.groupDmEnabled) {
|
||||
return true;
|
||||
}
|
||||
if (params.groupPolicy === "disabled") {
|
||||
return false;
|
||||
}
|
||||
const guildEntries = Object.values(params.guildEntries ?? {});
|
||||
if (guildEntries.length === 0) {
|
||||
return true;
|
||||
}
|
||||
return guildEntries.some((entry) => entry.reactionNotifications !== "off");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user