import { MatrixClient, MatrixEvent, Room } from 'matrix-js-sdk'; import { SoundboardPack } from './SoundboardPack'; import { LegacySoundboardContent, SoundboardClips, SoundboardContent, SoundboardRoomsContent, } from './types'; import { StateEvent } from '../../../types/matrix/room'; import { AccountDataEvent } from '../../../types/matrix/accountData'; import { getAccountData, getStateEvent, getStateEvents } from '../../utils/room'; /** Normalize a display name into a pack shortcode key (parallels emoji shortcodes). */ export function slugifyClipName(name: string): string { const s = name .trim() .toLowerCase() .replace(/\s+/g, '_') .replace(/[^a-z0-9_-]/g, ''); return s || 'clip'; } /** Pick a shortcode not already present in `taken`, suffixing on collision. */ export function uniqueShortcode(base: string, taken: Set): string { const code = slugifyClipName(base); if (!taken.has(code)) return code; let i = 2; while (taken.has(`${code}-${i}`)) i += 1; return `${code}-${i}`; } export function makeSoundboardPacks(packEvents: MatrixEvent[]): SoundboardPack[] { return packEvents.reduce((packs, packEvent) => { const packId = packEvent.getId(); if (!packId) return packs; packs.push(SoundboardPack.fromMatrixEvent(packId, packEvent)); return packs; }, []); } export function getRoomSoundboardPack(room: Room, stateKey: string): SoundboardPack | undefined { const packEvent = getStateEvent(room, StateEvent.LotusSoundboardRoom, stateKey); if (!packEvent) return undefined; const packId = packEvent.getId(); if (!packId) return undefined; return SoundboardPack.fromMatrixEvent(packId, packEvent); } export function getRoomSoundboardPacks(room: Room): SoundboardPack[] { return makeSoundboardPacks(getStateEvents(room, StateEvent.LotusSoundboardRoom)); } export function getGlobalSoundboardPacks(mx: MatrixClient): SoundboardPack[] { const content = getAccountData(mx, AccountDataEvent.LotusSoundboardRooms)?.getContent() as | SoundboardRoomsContent | undefined; const roomIdToPackInfo = content?.rooms; if (typeof roomIdToPackInfo !== 'object' || !roomIdToPackInfo) return []; return Object.keys(roomIdToPackInfo).flatMap((roomId) => { if (typeof roomIdToPackInfo[roomId] !== 'object') return []; const room = mx.getRoom(roomId); if (!room) return []; const stateKeys = roomIdToPackInfo[roomId]; const globalEvents = getStateEvents(room, StateEvent.LotusSoundboardRoom).filter((mE) => { const stateKey = mE.getStateKey(); return typeof stateKey === 'string' ? !!stateKeys[stateKey] : false; }); return makeSoundboardPacks(globalEvents); }); } /** * Convert a personal soundboard account-data content to the v2 pack shape, * migrating the v1 flat-list form (`{clips: [{id,name,url}]}`) on the fly. */ export function migrateUserSoundboardContent(raw: unknown): SoundboardContent { if (typeof raw !== 'object' || raw === null) return {}; const legacy = raw as LegacySoundboardContent; if (!Array.isArray(legacy.clips)) return raw as SoundboardContent; // already v2 (or empty) const clips: SoundboardClips = {}; const taken = new Set(); legacy.clips.forEach((c) => { if (!c || typeof c.url !== 'string') return; const shortcode = uniqueShortcode(c.name || 'clip', taken); taken.add(shortcode); clips[shortcode] = { url: c.url, body: c.name, info: { mimetype: c.mimetype, size: c.size }, }; }); return { pack: { display_name: 'My Soundboard' }, clips }; } export function getUserSoundboardPack(mx: MatrixClient): SoundboardPack | undefined { const packEvent = getAccountData(mx, AccountDataEvent.LotusSoundboard); const userId = mx.getUserId(); if (!packEvent || !userId) return undefined; const content = migrateUserSoundboardContent(packEvent.getContent()); return new SoundboardPack(userId, content, undefined); }