function defaultParsed(rawText = '') { const text = String(rawText || ''); return { rawText: text, prefixText: '', visibleText: text, displayText: text, blocks: [], replyRef: null, callSummary: null, }; } function hasTechPrefix(text = '', offset = 0) { return String(text || '').startsWith(' 0) { return `${hours}:${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`; } return `${minutes}:${String(seconds).padStart(2, '0')}`; } function buildCallDisplayText(callSummary) { if (!callSummary) return ''; if (callSummary.status === 'completed') { return `Звонок: ${formatCallDuration(callSummary.durationSec)}`; } const reason = String(callSummary.reason || '').trim().toLowerCase(); if (reason === 'offline') return 'Абонент не в сети'; if (reason === 'no_answer') return 'Нет ответа'; if (reason === 'connect_failed') return 'Не удалось установить соединение'; if (reason === 'busy') return 'Абонент занят'; if (reason === 'declined') return 'Звонок отклонён'; return 'Звонок не завершён'; } export function sanitizeUserDmTextForSend(rawText = '') { const text = String(rawText || ''); if (text.startsWith('`; } const cleanReason = String(reason || '').trim().toLowerCase(); return ``; } export function buildDmReplyTechBlock({ baseKey = '' } = {}) { const cleanBaseKey = String(baseKey || '').trim(); if (!cleanBaseKey) return ''; return ``; } export function parseDmTechBlocks(rawText = '') { const text = String(rawText || ''); if (!hasTechPrefix(text)) return defaultParsed(text); const blocks = []; let cursor = 0; let replyRef = null; let callSummary = null; while (hasTechPrefix(text, cursor)) { const end = text.indexOf('>', cursor); if (end < 0) { if (!blocks.length) return defaultParsed(text); break; } const prefixLength = getTechPrefixLength(text, cursor); if (!prefixLength) break; const inner = text.slice(cursor + prefixLength, end); const segments = inner.split(';').map((part) => String(part || '').trim()).filter(Boolean); if (!segments.length) { if (!blocks.length) return defaultParsed(text); break; } const kind = String(segments[0] || '').trim().toLowerCase(); const fields = {}; for (let i = 1; i < segments.length; i += 1) { const part = segments[i]; const eq = part.indexOf('='); if (eq <= 0) continue; const key = part.slice(0, eq).trim().toLowerCase(); const value = part.slice(eq + 1).trim(); if (key) fields[key] = value; } const block = { kind, version: Number(fields.v || 0), fields }; blocks.push(block); if (kind === 'reply' && !replyRef) { replyRef = parseReplyId(fields.id || ''); } else if (kind === 'call' && !callSummary) { const status = String(fields.status || '').trim().toLowerCase(); if (status === 'completed') { const durationSec = Math.max(0, Math.floor(Number(fields.duration || 0))); callSummary = { status: 'completed', durationSec, }; } else { callSummary = { status: 'failed', reason: String(fields.reason || '').trim().toLowerCase(), }; } } cursor = end + 1; } const visibleText = text.slice(cursor); const displayText = callSummary ? buildCallDisplayText(callSummary) : visibleText; return { rawText: text, prefixText: text.slice(0, cursor), visibleText, displayText, blocks, replyRef, callSummary, }; }