/** * Токен кастомного эмодзи сервера (AGENT.md 7.12). * * В тексте сообщения и в реакции эмодзи едет токеном `<:имя:file_id>` * (у анимированных — ``): имя разрешено только из латиницы, * цифр и подчёркиваний, `file_id` — снежинка. Единое описание формата живёт * здесь, чтобы разбор в разметке (`lib/markdown.ts`), подсказки композера и * настройки сервера не расходились. */ /** Имя эмодзи: ровно то, что принимает сервер при загрузке и переименовании. */ export const CUSTOM_EMOJI_NAME_SOURCE = '[a-zA-Z0-9_]{2,32}'; /** Токен целиком без якорей — вставляется в общий шаблон разметки. */ export const CUSTOM_EMOJI_TOKEN_SOURCE = ``; /** Проверка имени эмодзи на клиенте: до запроса, чтобы не гонять сервер. */ export const CUSTOM_EMOJI_NAME_PATTERN = new RegExp(`^${CUSTOM_EMOJI_NAME_SOURCE}$`, 'u'); const CUSTOM_EMOJI_TOKEN_PATTERN = new RegExp( `^<(?a?):(?${CUSTOM_EMOJI_NAME_SOURCE}):(?\\d+)>$`, 'u', ); /** Разобранный токен: имя, файл и признак анимации. */ export interface CustomEmojiToken { name: string; fileId: string; animated: boolean; } /** * Разбор токена. `null` — строка не является токеном эмодзи (битый или * неизвестный формат): вызывающий код показывает её как обычный текст. */ export function parseCustomEmoji(raw: string): CustomEmojiToken | null { const match = CUSTOM_EMOJI_TOKEN_PATTERN.exec(raw); const groups = match?.groups; const name = groups?.['name']; const fileId = groups?.['fileId']; if (name === undefined || fileId === undefined) { return null; } return { name, fileId, animated: groups?.['animated'] === 'a' }; } /** Короткий код эмодзи — то, что видит пользователь: `:имя:`. */ export function customEmojiShortcode(name: string): string { return `:${name}:`; } /** Токен для вставки в текст сообщения или реакцию. */ export function customEmojiToken(name: string, fileId: string, animated = false): string { return `<${animated ? 'a' : ''}:${name}:${fileId}>`; }