From eed828ec58164fc80455bac2b728d22cb02f0fd1 Mon Sep 17 00:00:00 2001 From: ares Date: Mon, 10 Aug 2026 00:43:30 +0300 Subject: [PATCH] =?UTF-8?q?=D0=BE=D1=82=D0=BF=D1=80=D0=B0=D0=B2=D0=BA?= =?UTF-8?q?=D0=B0=20=D1=84=D0=BE=D1=82=D0=BE=D0=B3=D1=80=D0=B0=D1=84=D0=B8?= =?UTF-8?q?=D0=B8=20=D0=BF=D1=80=D1=8F=D0=BC=20=D0=B2=20=D1=81=D0=BE=D0=BE?= =?UTF-8?q?=D0=B1=D1=89=D0=B5=D0=BD=D0=B8=D0=B8=20=D1=83=D0=B4=D0=B0=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=BE=D0=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/bot/panel.js | 44 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/src/bot/panel.js b/src/bot/panel.js index a1f6773..680d38d 100644 --- a/src/bot/panel.js +++ b/src/bot/panel.js @@ -78,7 +78,7 @@ async function renderItem(ctx, id) { const item = getCapture(id, ctx.from.id); if (!item) return edit(ctx, 'Запись не найдена (возможно, удалена по сроку хранения).', backTo('home')); - const caption = formatCapture(item); + const caption = clampCaption(formatCapture(item)); const back = backTo(`feed:${item.kind}:0`); if (item.media_path) { @@ -117,11 +117,14 @@ function formatCapture(item) { } function fileName(item) { - const ext = { photo: 'jpg', video: 'mp4', voice: 'ogg', gif: 'mp4', audio: 'mp3' }[item.media_kind] ?? 'bin'; + const mediaKind = item.media_kind ?? item.mediaKind; // строка БД — snake, объект перехвата — camel + const ext = { photo: 'jpg', video: 'mp4', voice: 'ogg', gif: 'mp4', audio: 'mp3' }[mediaKind] ?? 'bin'; return `${item.kind}_${item.id}.${ext}`; } const truncate = (s, n) => (s.length > n ? s.slice(0, n - 1) + '…' : s); +// Подпись к медиа у Telegram ограничена 1024 символами — режем с запасом. +const clampCaption = (s) => truncate(s, 1000); async function edit(ctx, text, keyboard) { try { @@ -138,19 +141,46 @@ async function edit(ctx, text, keyboard) { /** * Уведомляет ВЛАДЕЛЬЦА подключения о свежем перехвате — в его личку с ботом. * item.ownerChatId — куда слать (owner_chat_id из бизнес-подключения). - * Дёргается из модулей через контекст (ctx.capture → notifyCapture). + * + * Если медиа закэшировано, шлём его прямо в уведомлении ДОКУМЕНТОМ (не фото): + * документ Telegram не пережимает, поэтому картинка приходит в исходном качестве + * и её видно сразу — превью в чате, полный размер по тапу. Дёргается из модулей + * через контекст (ctx.capture → notifyCapture). item — объект перехвата (camelCase). */ export async function notifyCapture(bot, item) { if (!item.ownerChatId) { logger.warn(`bot: некому отправить уведомление о перехвате ${item.id} (нет ownerChatId)`); return; } - const kb = new InlineKeyboard().text('Открыть', `item:${item.id}`); - const head = '🗑 Удалённое сообщение'; - const text = `${head} от ${item.sender || 'неизвестно'}\n${item.media_kind ? `[${item.media_kind}] ` : ''}${truncate(item.text ?? '', 60)}`; + const kb = new InlineKeyboard().text('Открыть в панели', `item:${item.id}`); + const caption = notifyCaption(item); + + if (item.mediaPath) { + try { + const buffer = decrypt(await readMedia(item.mediaPath)); + await bot.api.sendDocument(item.ownerChatId, new InputFile(buffer, fileName(item)), { + caption, + reply_markup: kb, + }); + return; + } catch (err) { + logger.warn('bot: медиа для уведомления не отдалось, шлю текстом', err.message); + // ниже — запасной текстовый вариант + } + } + try { - await bot.api.sendMessage(item.ownerChatId, text, { reply_markup: kb }); + await bot.api.sendMessage(item.ownerChatId, caption, { reply_markup: kb }); } catch (err) { logger.warn('bot: не удалось уведомить владельца', err.message); } } + +/** Подпись к уведомлению: кто, тип медиа и текст — в пределах лимита подписи. */ +function notifyCaption(item) { + const mediaKind = item.media_kind ?? item.mediaKind; + const lines = [`🗑 Удалённое сообщение от ${item.sender || 'неизвестно'}`]; + if (mediaKind) lines.push(`[${mediaKind}]`); + if (item.text) lines.push('', item.text); + return clampCaption(lines.join('\n')); +}