/// declare const Buffer: any; import { MotionDocument, MotionEntry, ProfileDocument } from '../models'; import { avatarResponse, createProfile, dancerGradeResponse, makeUuid, motionInfoResponse, nowMs, readAvatar, readNumber, readStageResult, readString, } from '../utils'; function readMotionIds(reader: KDataReader): string[] { const values = reader .elements('motion_id_list.motion_id') .map(item => readString(item, '')); for (const dataItem of reader.elements('data_item')) { values.push( ...dataItem .elements('motion_id_list.motion_id') .map(item => readString(item, '')) ); } return values.filter( (value, index) => !!value && values.indexOf(value) === index ); } async function findProfile(refid: string): Promise { return ( (await DB.FindOne(refid, { collection: 'profile' })) || createProfile(refid, 'PLAYER') ); } export const saveMotionData: EPR = async (info, data, send) => { const reader = $(data); const refid = readString(reader, 'userid.ref_id'); if (!refid) { console.error('[Dance Around] save_motiondata is missing userid.ref_id'); return send.deny(); } const motionReader = reader.element('motiondata'); const full = motionReader.buffer('motion_full', Buffer.alloc(0)); const preview = motionReader.buffer('motion_prev', Buffer.alloc(0)); const entry: MotionEntry = { motionId: makeUuid(), active: true, publishSetting: readNumber(motionReader, 'publish_setting'), playCount: 1, totalGv: 0, playResult: readStageResult(motionReader), playDate: readNumber(motionReader, 'play_date', nowMs()), locationId: readString(motionReader, 'loc_id'), shopName: readString(motionReader, 'shopname'), avatar: readAvatar(motionReader, 'avatar'), motionFullBase64: full.toString('base64'), motionPreviewBase64: preview.toString('base64'), createdAt: nowMs(), }; const configuredLimit = Number(U.GetConfig('motion_history_limit')); const historyLimit = Number.isFinite(configuredLimit) ? Math.max(0, Math.min(100, Math.floor(configuredLimit))) : 20; if (historyLimit > 0) { const document = (await DB.FindOne(refid, { collection: 'motions', })) || { collection: 'motions' as const, schemaVersion: 1, entries: [], updatedAt: 0, }; document.entries = [entry, ...(document.entries || [])].slice( 0, historyLimit ); document.updatedAt = nowMs(); await DB.Upsert( refid, { collection: 'motions' }, { $set: document } ); } console.log( `[Dance Around] accepted motion ${entry.motionId} (${full.length}/${preview.length} bytes)` ); return send.object( { motion_id: K.ITEM('str', entry.motionId), }, { status: 0 } ); }; export const getMotionInfoList: EPR = async (info, data, send) => { const reader = $(data); const refid = readString(reader, 'userid.ref_id'); if (!refid) { console.error('[Dance Around] get_motioninfolist is missing userid.ref_id'); return send.deny(); } const document = await DB.FindOne(refid, { collection: 'motions', }); const entries = document ? document.entries || [] : []; return send.object( { data: entries.map(motionInfoResponse), }, { status: 0 } ); }; export const checkPlayableMotionData: EPR = async (info, data, send) => { const reader = $(data); const requested = readMotionIds(reader); const refid = readString(reader, 'ref_id'); const document = refid ? await DB.FindOne(refid, { collection: 'motions' }) : null; const available = new Set( (document ? document.entries || [] : []).map(entry => entry.motionId) ); return send.object( { data: requested.map(motionId => ({ motion_id: K.ITEM('str', motionId), is_playable: K.ITEM('bool', available.has(motionId)), })), }, { status: 0 } ); }; interface LocatedMotion { refid: string; entry: MotionEntry; profile: ProfileDocument; } async function locateMotions(motionIds: string[]): Promise { if (!motionIds.length) { return []; } const wanted = new Set(motionIds); const result: LocatedMotion[] = []; const documents = await DB.Find(null, { collection: 'motions', }); for (const document of documents) { const refid = (document as any).__refid as string; if (!refid) { continue; } const profile = await findProfile(refid); for (const entry of document.entries || []) { if (wanted.has(entry.motionId)) { result.push({ refid, entry, profile }); } } } return result; } export const getMotionData: EPR = async (info, data, send) => { const requested = readMotionIds($(data)); const located = await locateMotions(requested); return send.object( { data: located.map(item => ({ info: motionInfoResponse(item.entry), userid: { dancer_id: K.ITEM('str', item.profile.dancerId), }, profile: { name: K.ITEM('str', item.profile.name), }, privacy: { publish_setting: K.ITEM('s32', item.profile.privacy.publishSetting), }, dancer_grade: dancerGradeResponse(item.profile.dancerGrade), avatar: avatarResponse(item.entry.avatar), motiondata: { motion_full: K.ITEM( 'bin', Buffer.from(item.entry.motionFullBase64, 'base64') ), motion_prev: K.ITEM( 'bin', Buffer.from(item.entry.motionPreviewBase64, 'base64') ), }, })), }, { status: 0 } ); };