mirror of
https://github.com/openclaw/openclaw.git
synced 2026-02-19 18:39:20 -05:00
fix(discord): normalize bare numeric IDs in outbound target resolution
Bare numeric Discord IDs (e.g. '1470130713209602050') in cron delivery.to caused 'Ambiguous Discord recipient' errors and silent delivery failures. Adds normalizeDiscordOutboundTarget() to the existing Discord normalize module (channels/plugins/normalize/discord.ts) alongside normalizeDiscordMessagingTarget. Defaults bare numeric IDs to 'channel:<id>', matching existing behavior. Both the Discord extension plugin and standalone outbound adapter use the shared helper via a one-liner resolveTarget. Fixes #14753. Related: #13927
This commit is contained in:
@@ -16,6 +16,7 @@ import {
|
||||
migrateBaseNameToDefaultAccount,
|
||||
normalizeAccountId,
|
||||
normalizeDiscordMessagingTarget,
|
||||
normalizeDiscordOutboundTarget,
|
||||
PAIRING_APPROVED_MESSAGE,
|
||||
resolveDiscordAccount,
|
||||
resolveDefaultDiscordAccountId,
|
||||
@@ -291,6 +292,7 @@ export const discordPlugin: ChannelPlugin<ResolvedDiscordAccount> = {
|
||||
chunker: null,
|
||||
textChunkLimit: 2000,
|
||||
pollMaxOptions: 10,
|
||||
resolveTarget: ({ to }) => normalizeDiscordOutboundTarget(to),
|
||||
sendText: async ({ to, text, accountId, deps, replyToId, silent }) => {
|
||||
const send = deps?.sendDiscord ?? getDiscordRuntime().channel.discord.sendMessageDiscord;
|
||||
const result = await send(to, text, {
|
||||
|
||||
@@ -6,6 +6,29 @@ export function normalizeDiscordMessagingTarget(raw: string): string | undefined
|
||||
return target?.normalized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize a Discord outbound target for delivery. Bare numeric IDs are
|
||||
* prefixed with "channel:" to avoid the ambiguous-target error in
|
||||
* parseDiscordTarget. All other formats pass through unchanged.
|
||||
*/
|
||||
export function normalizeDiscordOutboundTarget(
|
||||
to?: string,
|
||||
): { ok: true; to: string } | { ok: false; error: Error } {
|
||||
const trimmed = to?.trim();
|
||||
if (!trimmed) {
|
||||
return {
|
||||
ok: false,
|
||||
error: new Error(
|
||||
'Discord recipient is required. Use "channel:<id>" for channels or "user:<id>" for DMs.',
|
||||
),
|
||||
};
|
||||
}
|
||||
if (/^\d+$/.test(trimmed)) {
|
||||
return { ok: true, to: `channel:${trimmed}` };
|
||||
}
|
||||
return { ok: true, to: trimmed };
|
||||
}
|
||||
|
||||
export function looksLikeDiscordTargetId(raw: string): boolean {
|
||||
const trimmed = raw.trim();
|
||||
if (!trimmed) {
|
||||
|
||||
35
src/channels/plugins/outbound/discord.test.ts
Normal file
35
src/channels/plugins/outbound/discord.test.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { normalizeDiscordOutboundTarget } from "../normalize/discord.js";
|
||||
|
||||
describe("normalizeDiscordOutboundTarget", () => {
|
||||
it("normalizes bare numeric IDs to channel: prefix", () => {
|
||||
expect(normalizeDiscordOutboundTarget("1470130713209602050")).toEqual({
|
||||
ok: true,
|
||||
to: "channel:1470130713209602050",
|
||||
});
|
||||
});
|
||||
|
||||
it("passes through channel: prefixed targets", () => {
|
||||
expect(normalizeDiscordOutboundTarget("channel:123")).toEqual({ ok: true, to: "channel:123" });
|
||||
});
|
||||
|
||||
it("passes through user: prefixed targets", () => {
|
||||
expect(normalizeDiscordOutboundTarget("user:123")).toEqual({ ok: true, to: "user:123" });
|
||||
});
|
||||
|
||||
it("passes through channel name strings", () => {
|
||||
expect(normalizeDiscordOutboundTarget("general")).toEqual({ ok: true, to: "general" });
|
||||
});
|
||||
|
||||
it("returns error for empty target", () => {
|
||||
expect(normalizeDiscordOutboundTarget("").ok).toBe(false);
|
||||
});
|
||||
|
||||
it("returns error for undefined target", () => {
|
||||
expect(normalizeDiscordOutboundTarget(undefined).ok).toBe(false);
|
||||
});
|
||||
|
||||
it("trims whitespace", () => {
|
||||
expect(normalizeDiscordOutboundTarget(" 123 ")).toEqual({ ok: true, to: "channel:123" });
|
||||
});
|
||||
});
|
||||
@@ -1,11 +1,13 @@
|
||||
import type { ChannelOutboundAdapter } from "../types.js";
|
||||
import { sendMessageDiscord, sendPollDiscord } from "../../../discord/send.js";
|
||||
import { normalizeDiscordOutboundTarget } from "../normalize/discord.js";
|
||||
|
||||
export const discordOutbound: ChannelOutboundAdapter = {
|
||||
deliveryMode: "direct",
|
||||
chunker: null,
|
||||
textChunkLimit: 2000,
|
||||
pollMaxOptions: 10,
|
||||
resolveTarget: ({ to }) => normalizeDiscordOutboundTarget(to),
|
||||
sendText: async ({ to, text, accountId, deps, replyToId, silent }) => {
|
||||
const send = deps?.sendDiscord ?? sendMessageDiscord;
|
||||
const result = await send(to, text, {
|
||||
|
||||
@@ -328,6 +328,7 @@ export { discordOnboardingAdapter } from "../channels/plugins/onboarding/discord
|
||||
export {
|
||||
looksLikeDiscordTargetId,
|
||||
normalizeDiscordMessagingTarget,
|
||||
normalizeDiscordOutboundTarget,
|
||||
} from "../channels/plugins/normalize/discord.js";
|
||||
export { collectDiscordStatusIssues } from "../channels/plugins/status-issues/discord.js";
|
||||
|
||||
|
||||
Reference in New Issue
Block a user