UNPKG

umbot

Version:

Мультиплатформенный фреймворк для создания голосовых навыков и чат-ботов с единой бизнес-логикой. Встроенная поддержка ВКонтакте, Telegram, Viber, MAX, Яндекс Алисы, Маруси и Сбера SmartApp. Архитектура на адаптерах позволяет подключать любые другие платф

609 lines (608 loc) 20 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.rand = rand; exports.keysCount = keysCount; exports.similarText = similarText; exports.isFileSync = isFileSync; exports.getFileInfoSync = getFileInfoSync; exports.freadSync = freadSync; exports.fwriteSync = fwriteSync; exports.unlinkSync = unlinkSync; exports.isDirSync = isDirSync; exports.mkdirSync = mkdirSync; exports.saveDataSync = saveDataSync; exports.getFileInfo = getFileInfo; exports.isFile = isFile; exports.isDir = isDir; exports.mkdir = mkdir; exports.fread = fread; exports.fwrite = fwrite; exports.unlink = unlink; exports.saveData = saveData; exports.httpBuildQuery = httpBuildQuery; exports.stdin = stdin; /** * Модуль вспомогательных утилит * * Предоставляет набор утилит для: * - Работы с файловой системой * - Обработки текста * - Работы с HTTP-параметрами * - Взаимодействия с консолью */ const fs = __importStar(require("fs")); // Импорт fs/promises — легитимная работа с файловой системой (чтение/запись конфигов, логов, загрузок). const fsPromises = __importStar(require("fs/promises")); const readline = __importStar(require("readline")); const node_path_1 = require("node:path"); /** * Возвращает случайное число из заданного диапазона * * @param {number} min - Минимальное значение диапазона (включительно) * @param {number} max - Максимальное значение диапазона (включительно) * @returns {number} Случайное целое число из диапазона [min, max] * * @example * ```ts * rand(1, 10); // -> случайное число от 1 до 10 * rand(0, 1); // -> 0 или 1 * ``` */ function rand(min, max) { return Math.floor(Math.random() * (max - min + 1) + min); } /** * Метод, возвращающий количество ключей в объекте * @param obj */ function keysCount(obj) { // Такой вариант быстрее Object.keys, но до тех пор, пока не был delete. let count = 0; for (const _ in obj) { count++; } return count; } /** * Вычисляет процент схожести двух текстов * Использует алгоритм LCS (Longest Common Subsequence) * * @param {string} first - Первый текст для сравнения * @param {string} second - Второй текст для сравнения * @returns {number} Процент схожести от 0 до 100 * * @example * ```ts * similarText('привет', 'привт'); // -> ~90 * similarText('hello', 'world'); // -> ~20 * similarText('same', 'same'); // -> 100 * ``` */ function similarText(first, second) { if (first === second || (first.length === 0 && second.length === 0)) { return 100; } if (first.length === 0 || second.length === 0) { return 0; } // Helper function to calculate LCS length using dynamic programming const lcsLength = (shorter, longer) => { const dp = new Int32Array(longer.length + 1); dp.fill(0, 0, longer.length + 1); for (let i = 0; i < shorter.length; i++) { let prevDiag = 0; for (let j = 0; j < longer.length; j++) { const current = dp[j + 1]; dp[j + 1] = shorter[i] === longer[j] ? prevDiag + 1 : Math.max(dp[j + 1], dp[j]); prevDiag = current; } } return dp[longer.length]; }; // Ensure shorter string is first for optimization const [a, b] = first.length <= second.length ? [first, second] : [second, first]; const totalLength = first.length + second.length; return (lcsLength(a, b) * 200) / totalLength; } /** * Быстрое сравнение на то похож введенный текст на имя файла или нет * @param str */ function looksLikeFilePath(str) { const i = str.lastIndexOf('.'); return (i > 0 && // есть точка, и не в начале i < str.length - 1 && // не в конце str.length - i <= 6 && // расширение ≤5 символов (".js", ".json" и т.п.) /^\w+$/.test(str.slice(i + 1)) // расширение — только словесные символы ); } /** * Синхронно проверяет существование файла * * @param {string} file - Путь к проверяемому файлу * @returns {boolean} true, если файл существует и это файл, иначе false * * @example * ```ts * isFile('path/to/file.txt'); // -> true * isFile('path/to/directory'); // -> false * isFile('nonexistent.txt'); // -> false * ``` */ function isFileSync(file) { // Если в тексте нет точки, значит это явно не файл if (looksLikeFilePath(file)) { const fileInfo = getFileInfoSync(file); return !!(fileInfo.success && fileInfo.data?.isFile()); } return false; } /** * Синхронно возвращает информацию о файле * * @param {string} fileName - Путь к файлу * @returns {FileOperationResult<fs.Stats>} Результат операции с информацией о файле * * @example * ```ts * const result = getFileInfo('file.txt'); * if (result.success) { * console.log(result.data.size); // размер файла * console.log(result.data.mtime); // время последнего изменения * } else { * console.error(result.error); * } * ``` */ function getFileInfoSync(fileName) { try { const stats = fs.lstatSync(fileName); return { success: true, data: stats }; } catch (error) { return { success: false, error: error instanceof Error ? error : new Error('Unknown error'), }; } } /** * Синхронно читает содержимое файла * * @param {string} fileName - Путь к файлу * @returns {FileOperationResult<string>} Результат операции с содержимым файла * * @example * ```ts * const result = fread('file.txt'); * if (result.success) { * console.log(result.data); // содержимое файла * } else { * console.error(result.error); * } * ``` */ function freadSync(fileName) { try { const content = fs.readFileSync(fileName, 'utf-8'); return { success: true, data: content }; } catch (error) { return { success: false, error: error instanceof Error ? error : new Error('Failed to read file'), }; } } /** * Синхронно записывает данные в файл * * @param {string} fileName - Путь к файлу * @param {string | Uint8Array} fileContent - Содержимое для записи * @param {'w' | 'a'} [mode='w'] - Режим записи: * - 'w' - перезапись файла * - 'a' - добавление в конец файла * @returns {FileOperationResult<void>} Результат операции записи * * @example * ```ts * // Перезапись файла * fwrite('file.txt', 'new content'); * * // Добавление в конец файла * fwrite('file.txt', 'additional content', 'a'); * ``` */ function fwriteSync(fileName, fileContent, mode = 'w') { try { if (mode === 'w') { const tmpPath = `${fileName}.tmp`; fs.writeFileSync(tmpPath, fileContent); fs.renameSync(tmpPath, fileName); } else { fs.appendFileSync(fileName, fileContent); } return { success: true }; } catch (error) { return { success: false, error: error instanceof Error ? error : new Error('Failed to write file'), }; } } /** * Синхронно удаляет файл * * @param {string} fileName - Путь к файлу * @returns {FileOperationResult<void>} Результат операции удаления * * @example * ```ts * const result = unlink('file.txt'); * if (result.success) { * console.log('File deleted successfully'); * } else { * console.error(result.error); * } * ``` */ function unlinkSync(fileName) { try { fs.unlinkSync(fileName); return { success: true }; } catch (error) { return { success: false, error: error instanceof Error ? error : new Error('Failed to delete file'), }; } } /** * Синхронно проверяет существование директории * * @param {string} path - Путь к директории * @returns {boolean} true, если директория существует, иначе false * * @example * ```ts * isDir('path/to/directory'); // -> true * isDir('nonexistent/dir'); // -> false * ``` */ function isDirSync(path) { try { return fs.existsSync(path); } catch { return false; } } /** * Синхронно создает директорию * * @param {string} path - Путь к создаваемой директории * @param {fs.Mode} [mask='0774'] - Маска прав доступа * @returns {FileOperationResult<void>} Результат операции создания директории * * @example * ```ts * const result = mkdir('new/directory'); * if (result.success) { * console.log('Directory created successfully'); * } else { * console.error(result.error); * } * ``` */ function mkdirSync(path, mask = '0774') { try { fs.mkdirSync(path, mask); return { success: true }; } catch (error) { return { success: false, error: error instanceof Error ? error : new Error('Failed to create directory'), }; } } /** * Синхронно сохраняет данные в файл * @param {IDir} dir - Объект с путем и названием файла * @param {string} data - Сохраняемые данные * @param {string} mode - Режим записи * @param {TLoggerCb} errorLogger - Функция для логирования ошибок * @returns {boolean} true в случае успешного сохранения */ function saveDataSync(dir, data, mode, errorLogger) { if (!isDirSync(dir.path)) { mkdirSync(dir.path); } try { JSON.parse(data); } catch (e) { errorLogger?.(`Ошибка при сохранении данных в файл: "${dir.path}/${dir.fileName}", так как данные не в json формате. Ошибка: ${e.message}`, { error: e, data, mode, }); } const res = fwriteSync((0, node_path_1.join)(dir.path, dir.fileName), data, mode); if (!res.success) { errorLogger?.(`Ошибка при сохранении данных в файл: "${dir.path}/${dir.fileName}". Ошибка: ${res.error}`, { error: res.error, data, mode, }); return false; } return true; } /** * Синхронно возвращает информацию о файле * * @param {string} fileName - Путь к файлу * @returns {FileOperationResult<fs.Stats>} Результат операции с информацией о файле * * @example * ```ts * const result = getFileInfo('file.txt'); * if (result.success) { * console.log(result.data.size); // размер файла * console.log(result.data.mtime); // время последнего изменения * } else { * console.error(result.error); * } * ``` */ async function getFileInfo(fileName) { try { const stats = await fsPromises.stat(fileName); return { success: true, data: stats }; } catch (error) { return { success: false, error: error instanceof Error ? error : new Error('Unknown error'), }; } } /** * Синхронно проверяет существование файла * * @param {string} file - Путь к проверяемому файлу * @returns {boolean} true, если файл существует и это файл, иначе false * * @example * ```ts * isFile('path/to/file.txt'); // -> true * isFile('path/to/directory'); // -> false * isFile('nonexistent.txt'); // -> false * ``` */ async function isFile(file) { // Если в тексте нет точки, значит это явно не файл if (looksLikeFilePath(file)) { const fileInfo = await getFileInfo(file); return !!(fileInfo.success && fileInfo.data?.isFile()); } return false; } /** * Проверяет существование директории * @param path - Путь к директории * @returns Promise<boolean> - true если директория существует */ async function isDir(path) { try { const stat = await fsPromises.stat(path); return stat.isDirectory(); } catch { return false; } } /** * Создает директорию * @param path - Путь к создаваемой директории * @param mask - Маска прав доступа * @returns Promise с результатом операции */ async function mkdir(path, mask) { try { await fsPromises.mkdir(path, mask); return { success: true }; } catch (error) { return { success: false, error: error instanceof Error ? error : new Error('Failed to create directory'), }; } } /** * Читает файл * @param fileName - Путь к файлу * @returns Promise с результатом операции */ async function fread(fileName) { try { const content = await fsPromises.readFile(fileName); return { success: true, data: content }; } catch (error) { return { success: false, error: error instanceof Error ? error : new Error('Failed to read file'), }; } } /** * Записывает данные в файл * @param fileName - Путь к файлу * @param fileContent - Содержимое для записи * @param mode - Режим записи: 'w' - перезапись, 'a' - добавление * @returns Promise с результатом операции */ async function fwrite(fileName, fileContent, mode = 'w') { try { if (mode === 'w') { const tmpPath = `${fileName}.tmp`; await fsPromises.writeFile(tmpPath, fileContent); await fsPromises.rename(tmpPath, fileName); } else { await fsPromises.appendFile(fileName, fileContent); } return { success: true }; } catch (error) { return { success: false, error: error instanceof Error ? error : new Error('Failed to write file'), }; } } /** * Удаляет файл * @param fileName - Путь к файлу * @returns Promise с результатом операции */ async function unlink(fileName) { try { await fsPromises.unlink(fileName); return { success: true }; } catch (error) { return { success: false, error: error instanceof Error ? error : new Error('Failed to delete file'), }; } } /** * Сохраняет данные в файл * @param {IDir} dir - Объект с путем и названием файла * @param {string} data - Сохраняемые данные * @param {string} mode - Режим записи * @param {TLoggerCb} errorLogger - Функция для логирования ошибок * @returns {boolean} true в случае успешного сохранения */ async function saveData(dir, data, mode, errorLogger) { if (!(await isDir(dir.path))) { await mkdir(dir.path); } if (data.startsWith('{')) { try { JSON.parse(data); } catch (e) { errorLogger?.(`Ошибка при сохранении данных в файл: "${dir.path}/${dir.fileName}", так как данные не в json формате. Ошибка: ${e.message}`, { error: e, data, mode, }); } } const res = await fwrite((0, node_path_1.join)(dir.path, dir.fileName), data, mode); if (!res.success) { errorLogger?.(`Ошибка при сохранении данных в файл: "${dir.path}/${dir.fileName}". Ошибка: ${res.error}`, { error: res.error, data, mode, }); return false; } return true; } /** * Преобразует объект параметров в URL-строку запроса * * @param {IGetParams} formData - Объект с параметрами * @param {string} [separator='&'] - Разделитель параметров * @returns {string} URL-строка запроса * * @example * ```ts * const params = { * name: 'John Doe', * age: '25' * }; * * httpBuildQuery(params); * // -> 'name=John+Doe&age=25' * * httpBuildQuery(params, ';'); * // -> 'name=John+Doe;age=25' * ``` */ function httpBuildQuery(formData, separator = '&') { return Object.entries(formData) .map(([key, value]) => { const encodedKey = encodeURI(key); const encodedValue = encodeURI(String(value)).replace(/%20/g, '+'); return `${encodedKey}=${encodedValue}`; }) .join(separator); } /** * Читает введенные данные из консоли * * @returns {Promise<string>} Промис с введенной строкой * * @example * ```ts * // В консоли: * // > Enter your name: John * const name = await stdin(); // -> 'John' * ``` */ function stdin() { const rl = readline.createInterface({ input: process.stdin, output: process.stdout, }); return new Promise((resolve) => { rl.once('line', (input) => { rl.close(); resolve(input); }); }); }