Discord: resolve username targets for outbound sends
This commit is contained in:
parent
9daa846457
commit
e402a55003
@ -49,7 +49,7 @@ export async function sendMessageDiscord(
|
||||
const chunkMode = resolveChunkMode(cfg, "discord", accountInfo.accountId);
|
||||
const textWithTables = convertMarkdownTables(text ?? "", tableMode);
|
||||
const { token, rest, request } = createDiscordClient(opts, cfg);
|
||||
const recipient = parseRecipient(to);
|
||||
const recipient = await parseRecipient(to, { cfg, accountId: accountInfo.accountId });
|
||||
const { channelId } = await resolveChannelId(rest, recipient, request);
|
||||
let result: { id: string; channel_id: string } | { id: string | null; channel_id: string };
|
||||
try {
|
||||
@ -104,7 +104,7 @@ export async function sendStickerDiscord(
|
||||
): Promise<DiscordSendResult> {
|
||||
const cfg = loadConfig();
|
||||
const { rest, request } = createDiscordClient(opts, cfg);
|
||||
const recipient = parseRecipient(to);
|
||||
const recipient = await parseRecipient(to, { cfg, accountId: opts.accountId });
|
||||
const { channelId } = await resolveChannelId(rest, recipient, request);
|
||||
const content = opts.content?.trim();
|
||||
const stickers = normalizeStickerIds(stickerIds);
|
||||
@ -131,7 +131,7 @@ export async function sendPollDiscord(
|
||||
): Promise<DiscordSendResult> {
|
||||
const cfg = loadConfig();
|
||||
const { rest, request } = createDiscordClient(opts, cfg);
|
||||
const recipient = parseRecipient(to);
|
||||
const recipient = await parseRecipient(to, { cfg, accountId: opts.accountId });
|
||||
const { channelId } = await resolveChannelId(rest, recipient, request);
|
||||
const content = opts.content?.trim();
|
||||
const payload = normalizeDiscordPollInput(poll);
|
||||
|
||||
@ -3,7 +3,7 @@ import { PollLayoutType } from "discord-api-types/payloads/v10";
|
||||
import type { RESTAPIPoll } from "discord-api-types/rest/v10";
|
||||
import { Routes } from "discord-api-types/v10";
|
||||
|
||||
import { loadConfig } from "../config/config.js";
|
||||
import { loadConfig, type ClawdbotConfig } from "../config/config.js";
|
||||
import type { RetryConfig } from "../infra/retry.js";
|
||||
import { createDiscordRetryRunner, type RetryRunner } from "../infra/retry-policy.js";
|
||||
import { normalizePollDurationHours, normalizePollInput, type PollInput } from "../polls.js";
|
||||
@ -15,6 +15,8 @@ import { fetchChannelPermissionsDiscord, isThreadChannelType } from "./send.perm
|
||||
import { DiscordSendError } from "./send.types.js";
|
||||
import { parseDiscordTarget } from "./targets.js";
|
||||
import { normalizeDiscordToken } from "./token.js";
|
||||
import { listDiscordDirectoryPeersLive } from "./directory-live.js";
|
||||
import { looksLikeDiscordTargetId } from "../channels/plugins/normalize/discord.js";
|
||||
|
||||
const DISCORD_TEXT_LIMIT = 2000;
|
||||
const DISCORD_MAX_STICKERS = 3;
|
||||
@ -91,14 +93,68 @@ function normalizeReactionEmoji(raw: string) {
|
||||
return encodeURIComponent(identifier);
|
||||
}
|
||||
|
||||
function parseRecipient(raw: string): DiscordRecipient {
|
||||
async function parseRecipient(
|
||||
raw: string,
|
||||
params: { cfg: ClawdbotConfig; accountId?: string },
|
||||
): Promise<DiscordRecipient> {
|
||||
const target = parseDiscordTarget(raw, {
|
||||
ambiguousMessage: `Ambiguous Discord recipient "${raw.trim()}". Use "user:${raw.trim()}" for DMs or "channel:${raw.trim()}" for channel messages.`,
|
||||
});
|
||||
if (!target) {
|
||||
throw new Error("Recipient is required for Discord sends");
|
||||
}
|
||||
return { kind: target.kind, id: target.id };
|
||||
if (target.kind === "user") {
|
||||
return { kind: target.kind, id: target.id };
|
||||
}
|
||||
if (looksLikeDiscordTargetId(raw) || /^\d+$/.test(target.id)) {
|
||||
return { kind: target.kind, id: target.id };
|
||||
}
|
||||
|
||||
const query = raw.trim();
|
||||
if (!query) {
|
||||
throw new Error("Recipient is required for Discord sends");
|
||||
}
|
||||
|
||||
const candidates = await listDiscordDirectoryPeersLive({
|
||||
cfg: params.cfg,
|
||||
accountId: params.accountId,
|
||||
query,
|
||||
limit: 5,
|
||||
});
|
||||
if (candidates.length === 0) {
|
||||
throw new Error(
|
||||
`Discord recipient "${raw.trim()}" did not match a user. Use "user:<id>" or "<@id>" for DMs.`,
|
||||
);
|
||||
}
|
||||
|
||||
const normalizedQuery = query.toLowerCase();
|
||||
const exactMatches = candidates.filter((entry) => {
|
||||
const name = entry.name?.trim().toLowerCase();
|
||||
const handle = entry.handle?.trim().toLowerCase().replace(/^@/, "");
|
||||
const id = entry.id?.trim().toLowerCase().replace(/^user:/, "");
|
||||
return name === normalizedQuery || handle === normalizedQuery || id === normalizedQuery;
|
||||
});
|
||||
|
||||
const selected =
|
||||
exactMatches.length === 1
|
||||
? exactMatches[0]
|
||||
: candidates.length === 1
|
||||
? candidates[0]
|
||||
: null;
|
||||
|
||||
if (!selected) {
|
||||
throw new Error(
|
||||
`Multiple Discord users matched "${raw.trim()}". Use "user:<id>" or "<@id>" to disambiguate.`,
|
||||
);
|
||||
}
|
||||
|
||||
const resolvedId = selected.id.replace(/^user:/i, "").trim();
|
||||
if (!resolvedId) {
|
||||
throw new Error(
|
||||
`Discord recipient "${raw.trim()}" did not match a valid user id. Use "user:<id>" or "<@id>".`,
|
||||
);
|
||||
}
|
||||
return { kind: "user", id: resolvedId };
|
||||
}
|
||||
|
||||
function normalizeStickerIds(raw: string[]) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user