function defaultParsed(rawText = '') { const text = String(rawText || ''); return { rawText: text, visibleText: text, displayText: text, blocks: [], replyRef: null, callSummary: null, }; } function parseReplyId(value = '') { const parts = String(value || '').split('|'); if (parts.length !== 4) return null; const fromLogin = String(parts[0] || '').trim(); const toLogin = String(parts[1] || '').trim(); const timeMs = Number(parts[2] || 0); const nonce = Number(parts[3] || 0); if (!fromLogin || !toLogin || !Number.isFinite(timeMs) || timeMs <= 0 || !Number.isFinite(nonce) || nonce < 0) { return null; } return { fromLogin, toLogin, timeMs, nonce, baseKey: `${fromLogin}|${toLogin}|${timeMs}|${nonce}`, }; } function formatCallDuration(totalSeconds) { const total = Math.max(0, Math.floor(Number(totalSeconds || 0))); const hours = Math.floor(total / 3600); const minutes = Math.floor((total % 3600) / 60); const seconds = total % 60; if (hours > 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 || ''); return text.startsWith('`; } const cleanReason = String(reason || '').trim().toLowerCase(); return ``; } export function parseDmTechBlocks(rawText = '') { const text = String(rawText || ''); if (!text.startsWith('', cursor); if (end < 0) { if (!blocks.length) return defaultParsed(text); break; } const inner = text.slice(cursor + 7, 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, visibleText, displayText, blocks, replyRef, callSummary, }; }