umbot
Version:
Мультиплатформенный фреймворк для создания голосовых навыков и чат-ботов с единой бизнес-логикой. Встроенная поддержка ВКонтакте, Telegram, Viber, MAX, Яндекс Алисы, Маруси и Сбера SmartApp. Архитектура на адаптерах позволяет подключать любые другие платф
236 lines (235 loc) • 9.69 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.getImageToken = getImageToken;
exports.getSoundToken = getSoundToken;
exports.getPause = getPause;
exports.replaceSound = replaceSound;
exports.removeSound = removeSound;
exports.defaultSoundProcessing = defaultSoundProcessing;
exports.initUserCommand = initUserCommand;
exports.getCorrectButtons = getCorrectButtons;
exports.tryParse = tryParse;
const models_1 = require("../../../models");
const utils_1 = require("../../../utils");
/**
* Возвращает токен для изображения.
* В случае, если найти токен в базе не удалось, отрабатывает обработчик, который отправляет запрос на получение токена.
* @param path Путь до изображения
* @param platform Платформа для которой нужно получить токен
* @param controller Контроллер приложения
* @param cb Обработчик, который вернет токен.
*/
async function getImageToken(path, platform, controller, cb) {
if (path) {
const model = new models_1.ImageTokens(controller.appContext);
model.platform = platform;
model.path = path;
const query = await model.whereOne({
platform,
path,
});
if (query && model.imageToken) {
return model.imageToken;
}
return cb(model);
}
return null;
}
/**
* Возвращает токен для аудио.
* В случае, если найти токен в базе не удалось, отрабатывает обработчик, который отправляет запрос на получение токена.
* @param path Путь до аудиофайла
* @param platform Платформа для которой нужно получить токен
* @param controller Контроллер приложения
* @param cb Обработчик, который вернет токен.
*/
async function getSoundToken(path, platform, controller, cb) {
if (path) {
const model = new models_1.SoundTokens(controller.appContext);
model.platform = platform;
model.path = path;
const query = await model.whereOne({
platform,
path,
});
if (query && model.soundToken) {
return model.soundToken;
}
return cb(model);
}
return null;
}
const PAUSE_REG = /#pause_<\[(\d+)]>#/g;
/**
* Ищет в запросе команду для паузы, и заменяет ее на корректный вид(sil).
*
* @param {string} text - Текст, который будет озвучен пользователю
* @returns {string} - Строка с паузой в формате sil <[ms]>
*/
function getPause(text) {
return text.replace(PAUSE_REG, (_, ms) => `sil <[${ms}]>`);
}
/**
* Заменяет звуковой токен в тексте на соответствующий звук
*
* @param {string} key - Ключ звука для замены
* @param {string | string[]} value - Значение или массив значений для замены
* @param {string} text - Исходный текст
* @returns {string} - Текст с замененными звуками
*
* @example
* ```ts
* // Замена одиночного звука
* const text = replaceSound(
* '#game_win#',
* '<speaker audio="alice-sounds-game-win-1.opus">',
* 'Поздравляем #game_win# с победой!'
* );
*
* // Замена на массив звуков
* const text = replaceSound(
* '#nature_rain#',
* [
* '<speaker audio="alice-sounds-nature-rain-1.opus">',
* '<speaker audio="alice-sounds-nature-rain-2.opus">'
* ],
* 'На улице #nature_rain# идет дождь'
* );
* ```
*/
function replaceSound(key, value, text) {
if (text.includes(key)) {
return utils_1.Text.textReplace(key, value, text);
}
return text;
}
/**
* Удаляет все звуковые токены из текста
*
* @param {string} text - Исходный текст
* @returns {string} - Текст без звуковых токенов
*
* @example
* ```ts
* // Удаление звуковых токенов
* const text = removeSound('Текст #game_win# без #nature_rain# звуков');
* // Результат: 'Текст без звуков'
* ```
*/
function removeSound(text) {
if (text.includes('speaker') || text.includes('sil')) {
return text.replace(/<speaker[^>]*>|sil\s*<\[\d+]>/gi, '');
}
return text;
}
/**
* Базовый метод для обработки tts.
* Основная задача метода - найти все ключи в запросе, и заменить их на корректные звуки/эффекты.
* По умолчанию используется в Алисе и Марусе.
* @param soundInfo - Информация необходимая для обработки аудио
* @param defaultSounds - Стандартные звуки
* @param defaultEffects - Стандартные эффекты
*/
/**
* Базовый метод для обработки tts.
* Основная задача метода - найти все ключи в запросе, и заменить их на корректные звуки/эффекты.
* По умолчанию используется в Алисе и Марусе.
* @param soundInfo - Информация необходимая для обработки аудио
* @param defaultSounds - Стандартные звуки
* @param defaultEffects - Стандартные эффекты
*/
function defaultSoundProcessing(soundInfo, defaultSounds, defaultEffects) {
let updSounds = [];
if (soundInfo.sounds.length) {
updSounds = [...soundInfo.sounds, ...(soundInfo.usedStandardSound ? defaultSounds : [])];
}
else if (soundInfo.usedStandardSound) {
updSounds = defaultSounds;
}
// Если в тексте нет "#", и никто не задал свои звуки,
// то считаем что звук никто не вставляет, поэтому доп обработка не требуется.
// По-хорошему, всегда стоит смотреть на наличие #, и при ее отсутствии не выполнять ничего, но могут быть места, где ключ может сильно отличаться.
const usedSoundEffect = soundInfo.text.includes('#');
if (usedSoundEffect) {
if (defaultEffects) {
defaultEffects.forEach((item) => {
soundInfo.text = soundInfo.text.replace(new RegExp(item.key, 'g'), item.effect);
});
}
soundInfo.text = getPause(soundInfo.text);
}
else if (!soundInfo.sounds.length) {
return soundInfo.text;
}
let res = soundInfo.text;
if (updSounds.length) {
for (let i = 0; i < updSounds.length; i++) {
const sound = updSounds[i];
if (typeof sound === 'object' &&
sound.sounds !== undefined &&
sound.key !== undefined) {
const sText = utils_1.Text.getText(sound.sounds);
if (sText) {
res = replaceSound(sound.key, sText, res);
}
}
}
}
return res;
}
/**
* Базовая функция, которая инициализирует команду пользователя.
* Обрабатывает различные типы запросов и сохраняет команду в контроллере
* @param request Объект запроса от пользователя
* @param controller Контроллер приложения
*/
function initUserCommand(request, controller) {
if (request.type === 'SimpleUtterance') {
controller.userCommand = request.command?.trim() || '';
controller.originalUserCommand = request.original_utterance.trim() || '';
}
else {
if (typeof request.payload === 'string') {
controller.userCommand = request.payload;
controller.originalUserCommand = request.payload;
}
else {
controller.userCommand = request.command?.trim() || '';
controller.originalUserCommand = request.original_utterance?.trim() || '';
}
controller.payload = request.payload;
}
if (!controller.userCommand) {
controller.userCommand = controller.originalUserCommand;
}
}
/**
* Возвращает корректный массив кнопок с учетом лимита
* @param buttons
* @param limit
*/
function getCorrectButtons(buttons, limit = 10) {
if (buttons && buttons.length > limit) {
return buttons.slice(0, limit);
}
return buttons;
}
/**
* Утилита для безопасного преобразования строки в объект
* @param data
*/
function tryParse(data) {
const rawPayload = data;
if (typeof rawPayload === 'string') {
const trimmedPayload = rawPayload.trim();
if (trimmedPayload[0] === '{' || trimmedPayload[0] === '[') {
try {
return JSON.parse(rawPayload);
}
catch {
return rawPayload;
}
}
}
return rawPayload;
}