umbot
Version:
Мультиплатформенный фреймворк для создания голосовых навыков и чат-ботов с единой бизнес-логикой. Встроенная поддержка ВКонтакте, Telegram, Viber, MAX, Яндекс Алисы, Маруси и Сбера SmartApp. Архитектура на адаптерах позволяет подключать любые другие платф
609 lines (608 loc) • 20 kB
JavaScript
;
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);
});
});
}