mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-11 02:44:34 +00:00
fix: resolve Discord usernames to user IDs for outbound messages
When sending Discord messages via cron jobs or the message tool, usernames like "john.doe" were incorrectly treated as channel names, causing silent delivery failures. This fix adds a resolveDiscordTarget() function that: - Queries Discord directory to resolve usernames to user IDs - Falls back to standard parsing for known formats - Enables sending DMs by username without requiring explicit user:ID format Changes: - Added resolveDiscordTarget() in targets.ts with directory lookup - Added parseAndResolveRecipient() in send.shared.ts - Updated all outbound send functions to use username resolution Fixes #2627
This commit is contained in:
@@ -5,8 +5,13 @@ import {
|
||||
type MessagingTarget,
|
||||
type MessagingTargetKind,
|
||||
type MessagingTargetParseOptions,
|
||||
type DirectoryConfigParams,
|
||||
type ChannelDirectoryEntry,
|
||||
} from "../channels/targets.js";
|
||||
|
||||
import { listDiscordDirectoryPeersLive } from "./directory-live.js";
|
||||
import { resolveDiscordAccount } from "./accounts.js";
|
||||
|
||||
export type DiscordTargetKind = MessagingTargetKind;
|
||||
|
||||
export type DiscordTarget = MessagingTarget;
|
||||
@@ -60,3 +65,60 @@ export function resolveDiscordChannelId(raw: string): string {
|
||||
const target = parseDiscordTarget(raw, { defaultKind: "channel" });
|
||||
return requireTargetKind({ platform: "Discord", target, kind: "channel" });
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a Discord username to user ID using the directory lookup.
|
||||
* This enables sending DMs by username instead of requiring explicit user IDs.
|
||||
*
|
||||
* @param raw - The username or raw target string (e.g., "john.doe")
|
||||
* @param options - Directory configuration params (cfg, accountId, limit)
|
||||
* @returns Parsed MessagingTarget with user ID, or undefined if not found
|
||||
*/
|
||||
export async function resolveDiscordTarget(
|
||||
raw: string,
|
||||
options: DirectoryConfigParams,
|
||||
): Promise<MessagingTarget | undefined> {
|
||||
const trimmed = raw.trim();
|
||||
if (!trimmed) return undefined;
|
||||
|
||||
// If already a known format, parse directly
|
||||
const directParse = parseDiscordTarget(trimmed, options);
|
||||
if (directParse && directParse.kind !== "channel" && !isLikelyUsername(trimmed)) {
|
||||
return directParse;
|
||||
}
|
||||
|
||||
// Try to resolve as a username via directory lookup
|
||||
try {
|
||||
const directoryEntries = await listDiscordDirectoryPeersLive({
|
||||
...options,
|
||||
query: trimmed,
|
||||
limit: 1,
|
||||
});
|
||||
|
||||
const match = directoryEntries[0];
|
||||
if (match && match.kind === "user") {
|
||||
// Extract user ID from the directory entry (format: "user:<id>")
|
||||
const userId = match.id.replace(/^user:/, "");
|
||||
return buildMessagingTarget("user", userId, trimmed);
|
||||
}
|
||||
} catch (error) {
|
||||
// Directory lookup failed - fall through to parse as-is
|
||||
// This preserves existing behavior for channel names
|
||||
}
|
||||
|
||||
// Fallback to original parsing (for channels, etc.)
|
||||
return parseDiscordTarget(trimmed, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a string looks like a Discord username (not a mention, prefix, or ID).
|
||||
* Usernames typically don't start with special characters except underscore.
|
||||
*/
|
||||
function isLikelyUsername(input: string): boolean {
|
||||
// Skip if it's already a known format
|
||||
if (/^(user:|channel:|discord:|@|<@!?)|[\d]+$/.test(input)) {
|
||||
return false;
|
||||
}
|
||||
// Likely a username if it doesn't match known patterns
|
||||
return true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user