























@@ -3,9 +3,13 @@ import { ChannelType } from "../internal/discord.js";
33import { createPartialDiscordChannelWithThrowingGetters } from "../test-support/partial-channel.js";
4455const transcribeFirstAudioMock = vi.hoisted(() => vi.fn());
6+const fetchPluralKitMessageInfoMock = vi.hoisted(() => vi.fn());
67const resolveDiscordDmCommandAccessMock = vi.hoisted(() => vi.fn());
78const handleDiscordDmCommandDecisionMock = vi.hoisted(() => vi.fn(async () => {}));
8910+vi.mock("../pluralkit.js", () => ({
11+fetchPluralKitMessageInfo: (...args: unknown[]) => fetchPluralKitMessageInfoMock(...args),
12+}));
913vi.mock("./preflight-audio.runtime.js", () => ({
1014transcribeFirstAudio: transcribeFirstAudioMock,
1115}));
@@ -45,6 +49,10 @@ beforeAll(async () => {
4549await import("./thread-bindings.js"));
4650});
475152+beforeEach(() => {
53+fetchPluralKitMessageInfoMock.mockReset();
54+});
55+4856function createThreadBinding(
4957overrides?: Partial<import("openclaw/plugin-sdk/conversation-runtime").SessionBindingRecord>,
5058) {
@@ -717,6 +725,77 @@ describe("preflightDiscordMessage", () => {
717725expect(result).toBeNull();
718726});
719727728+it("canonicalizes PluralKit webhook messages to the original Discord message id", async () => {
729+fetchPluralKitMessageInfoMock.mockResolvedValue({
730+id: "proxy-456",
731+original: "orig-123",
732+member: { id: "member-1", name: "Echo" },
733+system: { id: "system-1", name: "System" },
734+});
735+736+const result = await runGuildPreflight({
737+channelId: "c1",
738+guildId: "g1",
739+message: createDiscordMessage({
740+id: "proxy-456",
741+channelId: "c1",
742+content: "<@openclaw-bot> hello",
743+webhookId: "pluralkit-webhook-1",
744+author: {
745+id: "webhook-author",
746+bot: true,
747+username: "PluralKit",
748+},
749+mentionedUsers: [{ id: "openclaw-bot" }],
750+}),
751+discordConfig: {
752+pluralkit: { enabled: true },
753+} as DiscordConfig,
754+});
755+756+expect(fetchPluralKitMessageInfoMock).toHaveBeenCalledWith(
757+expect.objectContaining({
758+messageId: "proxy-456",
759+config: expect.objectContaining({ enabled: true }),
760+}),
761+);
762+expect(result).not.toBeNull();
763+expect(result?.sender.isPluralKit).toBe(true);
764+expect(result?.canonicalMessageId).toBe("orig-123");
765+});
766+767+it("skips PluralKit lookup for bound-thread webhook echoes", async () => {
768+const threadBinding = createThreadBinding({
769+targetKind: "session",
770+targetSessionKey: "agent:main:acp:discord-thread-1",
771+});
772+const threadId = "thread-webhook-pk-echo-1";
773+const parentId = "channel-parent-webhook-pk-echo-1";
774+775+const result = await runThreadBoundPreflight({
776+ threadId,
777+ parentId,
778+ threadBinding,
779+message: createDiscordMessage({
780+id: "m-webhook-pk-echo-1",
781+channelId: threadId,
782+content: "proxied user message",
783+webhookId: "pluralkit-webhook-1",
784+author: {
785+id: "relay-bot-1",
786+bot: true,
787+username: "Proxy",
788+},
789+}),
790+discordConfig: {
791+pluralkit: { enabled: true },
792+} as DiscordConfig,
793+});
794+795+expect(result).toBeNull();
796+expect(fetchPluralKitMessageInfoMock).not.toHaveBeenCalled();
797+});
798+720799it("bypasses mention gating in bound threads for allowed bot senders", async () => {
721800const threadBinding = createThreadBinding();
722801const threadId = "thread-bot-focus";
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。