import { emojiRegex } from "./emoji-regex.js"; import { fetchMeta } from "./fetch-meta.js"; import { Emojis } from "@/models/index.js"; import { toPunyNullable } from "./convert-host.js"; import { IsNull } from "typeorm"; const legacies = new Map([ ['like', '👍'], ['love', '❤️'], ['laugh', '😆'], ['hmm', '🤔'], ['surprise', '😮'], ['congrats', '🎉'], ['angry', '💢'], ['confused', '😥'], ['rip', '😇'], ['pudding', '🍮'], ['star', '⭐'], ]); export async function getFallbackReaction() { const meta = await fetchMeta(); return meta.defaultReaction; } export function convertLegacyReactions(reactions: Record) { const _reactions = new Map(); const decodedReactions = new Map(); for (const reaction in reactions) { if (reactions[reaction] <= 0) continue; let decodedReaction; if (decodedReactions.has(reaction)) { decodedReaction = decodedReactions.get(reaction); } else { decodedReaction = decodeReaction(reaction); decodedReactions.set(reaction, decodedReaction); } let emoji = legacies.get(decodedReaction.reaction); if (emoji) { _reactions.set(emoji, (_reactions.get(emoji) || 0) + reactions[reaction]); } else { _reactions.set(reaction, (_reactions.get(reaction) || 0) + reactions[reaction]); } } const _reactions2 = new Map(); for (const [reaction, count] of _reactions) { const decodedReaction = decodedReactions.get(reaction); _reactions2.set(decodedReaction.reaction, count); } return Object.fromEntries(_reactions2); } export async function toDbReaction( reaction?: string | null, reacterHost?: string | null, ): Promise { if (!reaction) return await getFallbackReaction(); reacterHost = toPunyNullable(reacterHost); // Convert string-type reactions to unicode const emoji = legacies.get(reaction) || (reaction === "♥️" ? "❤️" : null); if (emoji) return emoji; // Allow unicode reactions const match = emojiRegex.exec(reaction); if (match) { const unicode = match[0]; return unicode; } const custom = reaction.match(/^:([\w+-]+)(?:@\.)?:$/); if (custom) { const name = custom[1]; const emoji = await Emojis.findOneBy({ host: reacterHost || IsNull(), name, }); if (emoji) return reacterHost ? `:${name}@${reacterHost}:` : `:${name}:`; } return await getFallbackReaction(); } type DecodedReaction = { /** * リアクション名 (Unicode Emoji or ':name@hostname' or ':name@.') */ reaction: string; /** * name (カスタム絵文字の場合name, Emojiクエリに使う) */ name?: string; /** * host (カスタム絵文字の場合host, Emojiクエリに使う) */ host?: string | null; }; export function decodeReaction(str: string): DecodedReaction { const custom = str.match(/^:([\w+-]+)(?:@([\w.-]+))?:$/); if (custom) { const name = custom[1]; const host = custom[2] || null; return { reaction: `:${name}@${host || "."}:`, // ローカル分は@以降を省略するのではなく.にする name, host, }; } return { reaction: str, name: undefined, host: undefined, }; } export function convertLegacyReaction(reaction: string): string { const decoded = decodeReaction(reaction).reaction; if (legacies.has(decoded)) return legacies.get(decoded)!; return decoded; }