Files
arestools/src/core/media.js
T
ros 15a03a77fd Переписать проект в личный MTProto-юзербот
Смена архитектуры с бизнес-бота на Bot API (команды со слешем) на
личный юзербот на GramJS/MTProto с управлением через компаньон-бота.

Возможности:
- antidelete — кэш входящих в личке, восстановление удалённых «у всех»
- onetime — перехват «просмотр один раз» фото/видео до истечения ссылки
- локальное хранилище node:sqlite + шифрование AES-256-GCM, чистка по сроку
- компаньон-бот (grammY): инлайн-панель, лента с пагинацией, выдача медиа,
  доступ только владельцу по OWNER_ID

Устройство:
- core/ — crypto, db, media, loader, registry, logger
- userbot/ — MTProto-клиент из строки сессии, подписка на события
- modules/ — фичи как модули с хуками setup/onMessage/onEdited/onDeleted
- bot/ — компаньон-бот (единственный UI)
- scripts/login.js — генерация строки сессии

Удалён старый код Bot API (commands/, handlers/, middlewares/, dispatcher,
store). Обновлены README и .env.example. Hcrgram/ (сторонний референс)
добавлен в .gitignore.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-09 19:16:52 +03:00

61 lines
2.4 KiB
JavaScript
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.
import { mkdirSync } from 'node:fs';
import { readFile, writeFile } from 'node:fs/promises';
import { fileURLToPath } from 'node:url';
import { config, mediaPath } from '../config.js';
import { encrypt } from './crypto.js';
import { logger } from './logger.js';
/** Определяем тип медиа по объекту сообщения GramJS. */
export function detectMedia(message) {
if (!message?.media) return null;
if (message.photo) return 'photo';
if (message.video) return 'video';
if (message.voice) return 'voice';
if (message.videoNote) return 'video_note';
if (message.gif) return 'gif';
if (message.audio) return 'audio';
if (message.sticker) return 'sticker';
if (message.document) return 'document';
return 'media';
}
/**
* Одноразовые медиа помечены ttlSeconds на самом медиа. Такие файлы Telegram
* стирает у клиента после просмотра — их важно скачать в момент прихода.
*/
export function isOneTime(message) {
const media = message?.media;
const ttl = media?.ttlSeconds ?? media?.photo?.ttlSeconds ?? media?.document?.ttlSeconds;
return Boolean(ttl);
}
/**
* Скачивает медиа сообщения, шифрует и кладёт на диск. Возвращает
* { kind, path } либо null. Имя файла не раскрывает содержимого.
*/
export async function downloadEncrypted(client, message, tag = 'm') {
const kind = detectMedia(message);
if (!kind) return null;
try {
const buffer = await client.downloadMedia(message, {});
if (!buffer || !buffer.length) return { kind, path: null };
mkdirSync(mediaPath(), { recursive: true });
const name = `${tag}_${message.id ?? 'x'}_${buffer.length}.enc`;
const full = fileURLToPath(new URL(name, config.mediaDir));
await writeFile(full, encrypt(buffer));
logger.debug(`media: сохранено ${kind} -> ${name} (${buffer.length} б)`);
return { kind, path: name };
} catch (err) {
logger.warn(`media: не удалось скачать ${kind}:`, err.message);
return { kind, path: null };
}
}
/** Читает зашифрованный медиа-файл с диска по имени (как хранится в БД). */
export function readMedia(name) {
return readFile(fileURLToPath(new URL(name, config.mediaDir)));
}