import { existsSync, readFileSync } from "fs"; import { join } from "path"; export const HOME_CHAT_ANIMATION_CONFIG_KEY = "home_chat_animation_config"; export const HOME_CHAT_ANIMATION_MAX_BYTES = 1024 * 1024; export type HomeChatAnimationConfig = { enabled: boolean; fileName: string; htmlContent: string; updatedAt: string | null; }; export function normalizeHomeChatAnimationHtml(value: unknown): string { if (typeof value !== "string") { return ""; } return value .replace(/^\uFEFF/, "") .replace(/\u0000/g, "") .replace(/\u2028|\u2029/g, "\n"); } export function getHomeChatAnimationHtmlBytes(htmlContent: string): number { return Buffer.byteLength(htmlContent, "utf8"); } export function normalizeHomeChatAnimationConfig( value: unknown, ): HomeChatAnimationConfig { const raw = value && typeof value === "object" && !Array.isArray(value) ? (value as Record) : {}; const enabled = raw.enabled === true; const fileName = String(raw.fileName || "").trim().slice(0, 255); const htmlContent = normalizeHomeChatAnimationHtml(raw.htmlContent); const updatedAt = String(raw.updatedAt || "").trim() || null; return { enabled, fileName, htmlContent, updatedAt, }; } let cachedDefaultHomeAnimationHtml: string | null | undefined; export function getDefaultHomeChatAnimationHtml(): string { if (cachedDefaultHomeAnimationHtml !== undefined) { return cachedDefaultHomeAnimationHtml || ""; } const candidatePaths = [join(process.cwd(), "动画效果.html")]; for (const candidatePath of candidatePaths) { if (!existsSync(candidatePath)) { continue; } cachedDefaultHomeAnimationHtml = normalizeHomeChatAnimationHtml( readFileSync(candidatePath, "utf8"), ); return cachedDefaultHomeAnimationHtml; } cachedDefaultHomeAnimationHtml = ""; return ""; } export function resolvePublicHomeChatAnimationConfig( value: unknown, ): HomeChatAnimationConfig & { usesDefaultFallback: boolean } { const normalized = normalizeHomeChatAnimationConfig(value); const fallbackHtml = getDefaultHomeChatAnimationHtml(); const htmlContent = normalized.htmlContent || fallbackHtml; const usesDefaultFallback = !normalized.htmlContent && Boolean(fallbackHtml); if (!normalized.enabled || !htmlContent) { return { enabled: false, fileName: normalized.fileName, htmlContent: "", updatedAt: normalized.updatedAt, usesDefaultFallback, }; } return { enabled: true, fileName: normalized.fileName || (usesDefaultFallback ? "动画效果.html" : ""), htmlContent, updatedAt: normalized.updatedAt, usesDefaultFallback, }; }