godcrm/backend/services/briefing/formatters.js
GOD CRM Release f89e074dd1
Some checks failed
CI / Lint / Typecheck / Test / Build (push) Has been cancelled
CI / PostgreSQL Integration Tests (push) Has been cancelled
GOD CRM — public scrubbed snapshot
Governed substrate for autonomous agents: scoped identity (passports),
audited actions, MCP workspace. Infra IPs and secrets redacted for public release.
2026-08-10 04:01:45 +03:00

155 lines
7.6 KiB
JavaScript

/**
* Briefing — Message Formatters
*
* formatMorningBriefing, formatEveningCheckin
*/
import { getPriorityIcon, escTg } from './constants.js';
// =============================================================================
// MESSAGE FORMATTERS
// =============================================================================
export function formatMorningBriefing({ todayLabel, events, tasks, health, medications, recipientName }) {
const lines = [];
const name = recipientName || '\u0413\u0435\u0440\u0430';
lines.push(`\u2600\uFE0F *\u0414\u043e\u0431\u0440\u043e\u0435 \u0443\u0442\u0440\u043e, ${escTg(name)}!*`);
lines.push('');
lines.push(`\u{1F4C5} *\u0421\u0415\u0413\u041e\u0414\u041d\u042f* (${escTg(todayLabel)}):`);
lines.push('');
// Calendar
lines.push('\u{1F4C6} *\u041a\u0410\u041b\u0415\u041d\u0414\u0410\u0420\u042c:*');
if (events.length === 0) {
lines.push(' \u041d\u0435\u0442 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u043d\u0430 \u0441\u0435\u0433\u043e\u0434\u043d\u044f');
} else {
for (const event of events) {
const timeStr = event.time === '\u0432\u0435\u0441\u044c \u0434\u0435\u043d\u044c' ? '\u{1F30D} \u0432\u0435\u0441\u044c \u0434\u0435\u043d\u044c' : event.time;
const loc = event.location ? ` (\u{1F4CD}${escTg(event.location)})` : '';
lines.push(` ${timeStr} \u2014 ${escTg(event.title)}${loc}`);
}
}
lines.push('');
// Tasks
lines.push('\u{1F4CB} *\u0417\u0410\u0414\u0410\u0427\u0418 (Sprint):*');
if (tasks.length === 0) {
lines.push(' \u0412\u0441\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u044b! \u{1F389}');
} else {
const shown = tasks.slice(0, 15);
for (const task of shown) {
const icon = getPriorityIcon(task.priority);
const pLabel = task.priority ? String(task.priority).toUpperCase() + ': ' : '';
const stateStr = task.state ? ` [${escTg(task.state)}]` : '';
lines.push(` ${icon} ${pLabel}${escTg(task.title)}${stateStr}`);
}
if (tasks.length > 15) lines.push(` ... \u0438 \u0435\u0449\u0451 ${tasks.length - 15} \u0437\u0430\u0434\u0430\u0447`);
}
lines.push('');
// Medications
lines.push('\u{1F48A} *\u041b\u0415\u041a\u0410\u0420\u0421\u0422\u0412\u0410/\u0414\u041e\u0411\u0410\u0412\u041a\u0418:*');
if (medications.length === 0) {
lines.push(' \u041d\u0435\u0442 \u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439');
} else {
const groups = {};
for (const med of medications) {
const time = med.time_of_day || 'any';
if (!groups[time]) groups[time] = [];
const dosageStr = med.dosage ? ` (${escTg(med.dosage)})` : '';
groups[time].push(`${escTg(med.name)}${dosageStr}`);
}
const timeLabels = { morning: '\u0423\u0442\u0440\u043e', afternoon: '\u0414\u0435\u043d\u044c', evening: '\u0412\u0435\u0447\u0435\u0440', night: '\u041d\u043e\u0447\u044c', any: '\u041b\u044e\u0431\u043e\u0435 \u0432\u0440\u0435\u043c\u044f' };
for (const [time, meds] of Object.entries(groups)) {
lines.push(` ${timeLabels[time.toLowerCase()] || escTg(time)}: ${meds.join(', ')}`);
}
}
lines.push('');
// Health
lines.push('\u{1F4CA} *\u0417\u0414\u041e\u0420\u041e\u0412\u042c\u0415:*');
if (!health) {
lines.push(' \u041d\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0445');
} else {
if (health.streak !== undefined && health.streak !== null) lines.push(` Streak: ${health.streak} \u0434\u043d\u0435\u0439`);
if (health.weight) lines.push(` \u0412\u0435\u0441: ${health.weight} kg`);
if (health.steps) lines.push(` \u0428\u0430\u0433\u0438: ${health.steps}`);
if (health.sleep_hours) lines.push(` \u0421\u043e\u043d: ${health.sleep_hours}h`);
if (health.mood) lines.push(` \u041d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u0435: ${escTg(health.mood)}`);
if (health.energy) lines.push(` \u042d\u043d\u0435\u0440\u0433\u0438\u044f: ${health.energy}/10`);
const hasSpecific = health.streak || health.weight || health.steps || health.sleep_hours || health.mood || health.energy;
if (!hasSpecific) lines.push(` \u041f\u043e\u0441\u043b\u0435\u0434\u043d\u044f\u044f \u0437\u0430\u043f\u0438\u0441\u044c: ${health._recorded_at || 'N/A'}`);
}
lines.push('');
lines.push('\u0425\u043e\u0440\u043e\u0448\u0435\u0433\u043e \u0434\u043d\u044f! \u{1F4AA}');
return lines.join('\n');
}
export function formatEveningCheckin({ todayLabel, completedTasks, remainingTasks, medications, health }) {
const lines = [];
lines.push('\u{1F319} *\u0412\u0435\u0447\u0435\u0440\u043d\u0438\u0439 \u0447\u0435\u043a-\u0438\u043d*');
lines.push('');
lines.push(`\u{1F4C5} *\u0418\u0422\u041e\u0413\u0418 \u0414\u041d\u042f* (${escTg(todayLabel)}):`);
lines.push('');
// Completed
lines.push('\u2705 *\u0421\u0414\u0415\u041b\u0410\u041d\u041e:*');
if (completedTasks.length === 0) {
lines.push(' \u041d\u0435\u0442 \u0437\u0430\u0432\u0435\u0440\u0448\u0451\u043d\u043d\u044b\u0445 \u0437\u0430\u0434\u0430\u0447 \u0441\u0435\u0433\u043e\u0434\u043d\u044f');
} else {
for (const task of completedTasks) {
lines.push(` ${getPriorityIcon(task.priority)} ${escTg(task.title)}`);
}
}
lines.push('');
// Remaining
lines.push('\u{1F6A7} *\u041d\u0415 \u0417\u0410\u0412\u0415\u0420\u0428\u0415\u041d\u041e:*');
if (remainingTasks.length === 0) {
lines.push(' \u0412\u0441\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u044b! \u{1F3C6}');
} else {
const shown = remainingTasks.slice(0, 10);
for (const task of shown) {
const stateStr = task.state ? ` [${escTg(task.state)}]` : '';
lines.push(` ${getPriorityIcon(task.priority)} ${escTg(task.title)}${stateStr}`);
}
if (remainingTasks.length > 10) lines.push(` ... \u0438 \u0435\u0449\u0451 ${remainingTasks.length - 10} \u0437\u0430\u0434\u0430\u0447`);
}
lines.push('');
// Evening meds
lines.push('\u{1F48A} *\u0412\u0415\u0427\u0415\u0420\u041d\u0418\u0415 \u041b\u0415\u041a\u0410\u0420\u0421\u0422\u0412\u0410:*');
const eveningMeds = medications.filter(m => {
const t = (m.time_of_day || '').toLowerCase();
return t === 'evening' || t === 'night' || t === 'any';
});
if (eveningMeds.length === 0) {
lines.push(' \u041d\u0435\u0442 \u0432\u0435\u0447\u0435\u0440\u043d\u0438\u0445 \u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439');
} else {
lines.push(` ${eveningMeds.map(m => `${escTg(m.name)}${m.dosage ? ` (${escTg(m.dosage)})` : ''}`).join(', ')}`);
}
lines.push('');
// Health
lines.push('\u{1F4CA} *\u0417\u0414\u041e\u0420\u041e\u0412\u042c\u0415:*');
if (!health) {
lines.push(' \u041d\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0445');
} else {
if (health.streak !== undefined && health.streak !== null) lines.push(` Streak: ${health.streak} \u0434\u043d\u0435\u0439 \u{1F525}`);
if (health.mood) lines.push(` \u041d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u0435: ${escTg(health.mood)}`);
if (health.energy) lines.push(` \u042d\u043d\u0435\u0440\u0433\u0438\u044f: ${health.energy}/10`);
}
lines.push('');
// Productivity
const total = completedTasks.length + remainingTasks.length;
const pct = total > 0 ? Math.round((completedTasks.length / total) * 100) : 0;
lines.push(`\u{1F4C8} *\u041f\u0440\u043e\u0434\u0443\u043a\u0442\u0438\u0432\u043d\u043e\u0441\u0442\u044c:* ${completedTasks.length}/${total} \u0437\u0430\u0434\u0430\u0447 (${pct}%)`);
lines.push('');
lines.push('\u0421\u043f\u043e\u043a\u043e\u0439\u043d\u043e\u0439 \u043d\u043e\u0447\u0438! \u{1F303}');
return lines.join('\n');
}