Files
clawdbot/src/discord/send.reactions.ts
Peter Steinberger c379191f80 chore: migrate to oxlint and oxfmt
Co-authored-by: Christoph Nakazawa <christoph.pojer@gmail.com>
2026-01-14 15:02:19 +00:00

116 lines
3.6 KiB
TypeScript

import { Routes } from "discord-api-types/v10";
import { loadConfig } from "../config/config.js";
import {
buildReactionIdentifier,
createDiscordClient,
formatReactionEmoji,
normalizeReactionEmoji,
resolveDiscordRest,
} from "./send.shared.js";
import type { DiscordReactionSummary, DiscordReactOpts } from "./send.types.js";
export async function reactMessageDiscord(
channelId: string,
messageId: string,
emoji: string,
opts: DiscordReactOpts = {},
) {
const cfg = loadConfig();
const { rest, request } = createDiscordClient(opts, cfg);
const encoded = normalizeReactionEmoji(emoji);
await request(
() => rest.put(Routes.channelMessageOwnReaction(channelId, messageId, encoded)),
"react",
);
return { ok: true };
}
export async function removeReactionDiscord(
channelId: string,
messageId: string,
emoji: string,
opts: DiscordReactOpts = {},
) {
const rest = resolveDiscordRest(opts);
const encoded = normalizeReactionEmoji(emoji);
await rest.delete(Routes.channelMessageOwnReaction(channelId, messageId, encoded));
return { ok: true };
}
export async function removeOwnReactionsDiscord(
channelId: string,
messageId: string,
opts: DiscordReactOpts = {},
): Promise<{ ok: true; removed: string[] }> {
const rest = resolveDiscordRest(opts);
const message = (await rest.get(Routes.channelMessage(channelId, messageId))) as {
reactions?: Array<{ emoji: { id?: string | null; name?: string | null } }>;
};
const identifiers = new Set<string>();
for (const reaction of message.reactions ?? []) {
const identifier = buildReactionIdentifier(reaction.emoji);
if (identifier) identifiers.add(identifier);
}
if (identifiers.size === 0) return { ok: true, removed: [] };
const removed: string[] = [];
await Promise.allSettled(
Array.from(identifiers, (identifier) => {
removed.push(identifier);
return rest.delete(
Routes.channelMessageOwnReaction(channelId, messageId, normalizeReactionEmoji(identifier)),
);
}),
);
return { ok: true, removed };
}
export async function fetchReactionsDiscord(
channelId: string,
messageId: string,
opts: DiscordReactOpts & { limit?: number } = {},
): Promise<DiscordReactionSummary[]> {
const rest = resolveDiscordRest(opts);
const message = (await rest.get(Routes.channelMessage(channelId, messageId))) as {
reactions?: Array<{
count: number;
emoji: { id?: string | null; name?: string | null };
}>;
};
const reactions = message.reactions ?? [];
if (reactions.length === 0) return [];
const limit =
typeof opts.limit === "number" && Number.isFinite(opts.limit)
? Math.min(Math.max(Math.floor(opts.limit), 1), 100)
: 100;
const summaries: DiscordReactionSummary[] = [];
for (const reaction of reactions) {
const identifier = buildReactionIdentifier(reaction.emoji);
if (!identifier) continue;
const encoded = encodeURIComponent(identifier);
const users = (await rest.get(Routes.channelMessageReaction(channelId, messageId, encoded), {
limit,
})) as Array<{ id: string; username?: string; discriminator?: string }>;
summaries.push({
emoji: {
id: reaction.emoji.id ?? null,
name: reaction.emoji.name ?? null,
raw: formatReactionEmoji(reaction.emoji),
},
count: reaction.count,
users: users.map((user) => ({
id: user.id,
username: user.username,
tag:
user.username && user.discriminator
? `${user.username}#${user.discriminator}`
: user.username,
})),
});
}
return summaries;
}
export { fetchChannelPermissionsDiscord } from "./send.permissions.js";