Files
kvarta_project/apps/api/src/telegram/system.ts
T
nunuchek 88b6e1395e refactor: simplify — мёртвые поля контента, общие хелперы api и тестов, липкая CTA без site в бандле
landing:
- site.ts: убраны неиспользуемые roast, brew, priceDisplay, hero.eyebrow, footer.phone (Footer берёт CONTACT_PHONE)
- form.ts: убраны FORM_TEXT.title/lead, CTAS, CtaPreset
- StickyCta получает тексты пропсами от layout: клиентский чанк больше не тащит все тексты страницы
- LeadForm/useLeadForm: без неиспользуемой подмены sendLead и className; leadPrefill без счётчика seq
- Stamp без мёртвого tone; убраны data-interest, data-hot, класс lead-form (ничем не читались)
- одно LEAD_FIELDS на sendLead и leadFormModel; регэксп typo вынесен из вызова; дубль фона body

api:
- changedFields и Version общие для leads и outbox; countLeads и leadStamps без копий SQL
- errorMessage в system.ts вместо четырёх копий; take() для ожидающих подтверждений
- backup: один Intl.DateTimeFormat на модуль, без невызываемых параметров timeZone/hour
- без мёртвых LOOPBACK '::ffff:127.0.0.1', client.call, Pick<BackupCommands> со всеми ключами
- testing.ts: validLead, addLead, count, leadIds вместо копий в тестах

deploy.sh: deployed_field и цикл по REPOS вместо повторов имён
2026-10-17 17:31:11 +03:00

69 lines
2.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Тема «Kvarta Cafe: Система»: сбои и служебные события без персональных данных.
// Всё дублируется в stdout — это единственный канал, если Telegram недоступен или тема не задана.
import type { TelegramClient } from './client.js';
export const SYSTEM_THROTTLE_MS = 10 * 60 * 1000;
export type SystemReporter = {
/** Никогда не бросает: отчёт о сбое не должен порождать новый сбой. */
report(kind: string, text: string): Promise<void>;
};
export function createSystemReporter({
client,
chatId,
threadId,
now,
log = (line: string) => console.log(line),
}: {
client?: Pick<TelegramClient, 'sendMessage'>;
chatId?: number;
threadId?: number;
now: () => Date;
log?: (line: string) => void;
}): SystemReporter {
// Одинаковые события не чаще раза в 10 минут; пропущенные копим и показываем с ближайшим отправленным.
const throttle = new Map<string, { sentAt: number; skipped: number }>();
return {
async report(kind, text) {
log(`[system] ${kind}: ${text}`);
if (!client || chatId === undefined || threadId === undefined) return;
const t = now().getTime();
const state = throttle.get(kind);
if (state && t - state.sentAt < SYSTEM_THROTTLE_MS) {
state.skipped++;
return;
}
const count = (state?.skipped ?? 0) + 1;
throttle.set(kind, { sentAt: t, skipped: 0 });
try {
await client.sendMessage({
chat_id: chatId,
message_thread_id: threadId,
text: count > 1 ? `×${count} за период · ${text}` : text,
});
} catch (err) {
log(`[system] не отправлено в Telegram: ${errorMessage(err)}`);
}
},
};
}
export function errorMessage(err: unknown): string {
return err instanceof Error ? err.message : String(err);
}
/** Короткий stack без путей до node_modules: в «Систему» и stdout. */
export function shortError(err: unknown): string {
if (!(err instanceof Error)) return String(err);
const frames = (err.stack ?? '')
.split('\n')
.slice(1)
.filter((l) => !l.includes('node_modules') && !l.includes('node:'))
.slice(0, 3)
.map((l) => l.trim());
return [`${err.name}: ${err.message}`, ...frames].join('\n');
}