{"id":13633432,"url":"https://github.com/vitalets/alice-renderer","last_synced_at":"2025-04-30T17:41:47.637Z","repository":{"id":47741599,"uuid":"172253324","full_name":"vitalets/alice-renderer","owner":"vitalets","description":"Node.js библиотека для формирования ответов в навыках Яндекс Алисы.","archived":false,"fork":false,"pushed_at":"2022-11-25T16:02:08.000Z","size":663,"stargazers_count":32,"open_issues_count":6,"forks_count":5,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-09T11:04:57.297Z","etag":null,"topics":["alice","alice-sdk","alice-skills","yandex"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vitalets.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-02-23T19:12:51.000Z","updated_at":"2025-03-29T02:28:55.000Z","dependencies_parsed_at":"2023-01-22T07:16:20.998Z","dependency_job_id":null,"html_url":"https://github.com/vitalets/alice-renderer","commit_stats":null,"previous_names":[],"tags_count":25,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vitalets%2Falice-renderer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vitalets%2Falice-renderer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vitalets%2Falice-renderer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vitalets%2Falice-renderer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vitalets","download_url":"https://codeload.github.com/vitalets/alice-renderer/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249543297,"owners_count":21288703,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["alice","alice-sdk","alice-skills","yandex"],"created_at":"2024-08-01T23:00:42.912Z","updated_at":"2025-04-18T20:31:22.546Z","avatar_url":"https://github.com/vitalets.png","language":"JavaScript","funding_links":[],"categories":["Разработка"],"sub_categories":["SDK"],"readme":"# alice-renderer\n\n[![Actions Status](https://github.com/vitalets/alice-renderer/workflows/autotests/badge.svg)](https://github.com/vitalets/alice-renderer/actions)\n[![Coverage Status](https://coveralls.io/repos/github/vitalets/alice-renderer/badge.svg?branch=master)](https://coveralls.io/github/vitalets/alice-renderer?branch=master)\n[![Known Vulnerabilities](https://snyk.io/test/github/vitalets/alice-renderer/badge.svg?targetFile=package.json)](https://snyk.io/test/github/vitalets/alice-renderer?targetFile=package.json)\n[![npm](https://img.shields.io/npm/v/alice-renderer.svg)](https://www.npmjs.com/package/alice-renderer)\n[![license](https://img.shields.io/npm/l/alice-renderer.svg)](https://www.npmjs.com/package/alice-renderer)\n\nNode.js библиотека для формирования [ответов](https://tech.yandex.ru/dialogs/alice/doc/protocol-docpage/#response) в навыках Яндекс Алисы.  \n\nПозволяет:\n* компактно записывать ответ\n* разделять данные на текст и голос (там где нужно)\n* добавлять паузы, звуки и аудио-эффекты\n* вставлять изображения\n* добавлять вариативность ответов\n* вставлять кнопки-подсказки\n\nОснована на использовании [Tagged templates](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Tagged_templates).\n\n## Содержание\n\n\u003c!-- toc --\u003e\n\n- [Установка](#%D1%83%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%BA%D0%B0)\n- [Использование](#%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5)\n  * [Базовый пример](#%D0%B1%D0%B0%D0%B7%D0%BE%D0%B2%D1%8B%D0%B9-%D0%BF%D1%80%D0%B8%D0%BC%D0%B5%D1%80)\n  * [Пример с модификаторами](#%D0%BF%D1%80%D0%B8%D0%BC%D0%B5%D1%80-%D1%81-%D0%BC%D0%BE%D0%B4%D0%B8%D1%84%D0%B8%D0%BA%D0%B0%D1%82%D0%BE%D1%80%D0%B0%D0%BC%D0%B8)\n  * [Пример с параметрами](#%D0%BF%D1%80%D0%B8%D0%BC%D0%B5%D1%80-%D1%81-%D0%BF%D0%B0%D1%80%D0%B0%D0%BC%D0%B5%D1%82%D1%80%D0%B0%D0%BC%D0%B8)\n- [API](#api)\n  * [reply](#reply)\n  * [reply.end](#replyend)\n  * [buttons(items, [defaults])](#buttonsitems-defaults)\n  * [audio(name)](#audioname)\n  * [effect(name)](#effectname)\n  * [image(imageId, [options])](#imageimageid-options)\n  * [pause([ms])](#pausems)\n  * [br([count])](#brcount)\n  * [text(value)](#textvalue)\n  * [tts(value)](#ttsvalue)\n  * [textTts(textValue, ttsValue)](#textttstextvalue-ttsvalue)\n  * [plural(number, one, two, five)](#pluralnumber-one-two-five)\n  * [enumerate(arr, { separator = ', ', lastSeparator = ' или ' })](#enumeratearr--separator----lastseparator---%D0%B8%D0%BB%D0%B8--)\n  * [userify(userId, target)](#userifyuserid-target)\n  * [select(array)](#selectarray)\n  * [once(options, response)](#onceoptions-response)\n  * [configure(options)](#configureoptions)\n- [Рецепты](#%D1%80%D0%B5%D1%86%D0%B5%D0%BF%D1%82%D1%8B)\n  * [Вариативность через массивы](#%D0%B2%D0%B0%D1%80%D0%B8%D0%B0%D1%82%D0%B8%D0%B2%D0%BD%D0%BE%D1%81%D1%82%D1%8C-%D1%87%D0%B5%D1%80%D0%B5%D0%B7-%D0%BC%D0%B0%D1%81%D1%81%D0%B8%D0%B2%D1%8B)\n  * [Модуль рендеринга ответов](#%D0%BC%D0%BE%D0%B4%D1%83%D0%BB%D1%8C-%D1%80%D0%B5%D0%BD%D0%B4%D0%B5%D1%80%D0%B8%D0%BD%D0%B3%D0%B0-%D0%BE%D1%82%D0%B2%D0%B5%D1%82%D0%BE%D0%B2)\n  * [Обработка условий](#%D0%BE%D0%B1%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%BA%D0%B0-%D1%83%D1%81%D0%BB%D0%BE%D0%B2%D0%B8%D0%B9)\n- [Лицензия](#%D0%BB%D0%B8%D1%86%D0%B5%D0%BD%D0%B7%D0%B8%D1%8F)\n\n\u003c!-- tocstop --\u003e\n\n## Установка\n```bash\nnpm i alice-renderer\n```\n\n## Использование\n### Базовый пример\nВ простейшем варианте нужно применить тег-функцию [`reply`](#reply) к некоторой строке, заключённой в backticks `` ` ``:\n```js\nreply`строка`;\n```\nВ результате получим объект с полями `text` и `tts`, в которых записана переданная строка:\n```json5\n{\n  text: 'строка',\n  tts: 'строка',\n  end_session: false\n}\n```\nПри этом для текстового представления из строки вырезаются акценты (`+`):\n```js\nconst { reply } = require('alice-renderer');\n\nconst response = reply`Как дел+а?`;\n\nconsole.log(response);\n\n/*\n{\n  text: 'Как дела?',\n  tts: 'Как дел+а?',\n  end_session: false\n}\n*/\n```\n\n### Пример с модификаторами\nФункции-модификаторы позволяют обогащать ответ отдельно в текстовом и голосовом каналах.\nНапример, модификатор [`audio`](#audioname) добавляет звук - он запишется только в поле `tts`:\n```js\nconst { reply, audio } = require('alice-renderer');\n\nreply`${audio('sounds-game-win-1')} Как дел+а?`;\n\n/*\n{\n  text: 'Как дела?',\n  tts: '\u003cspeaker audio=\"alice-sounds-game-win-1.opus\"\u003e Как дел+а?',\n  end_session: false\n}\n*/\n```\n\nМодификатор [`buttons`](#buttonsitems-defaults) позволяет добавить кнопки:\n```js\nconst { reply, buttons } = require('alice-renderer');\n\nreply`\n  Как дел+а?\n  ${buttons(['Отлично', 'Супер'])}\n`;\n\n/*\n{\n  text: 'Как дела?',\n  tts: 'Как дел+а?',\n  buttons: [\n    {title: 'Отлично', hide: true},\n    {title: 'Супер', hide: true},\n  ],\n  end_session: false\n}\n*/\n```\n\nЧтобы сделать ответ более разнообразным можно передавать в `reply` массивы значений:`${[item1, item2, ...]}`.\nПри рендеренге из массива выберется один случайный элемент:\n```js\nconst { reply } = require('alice-renderer');\n\nreply`\n  ${['Привет', 'Здор+ово']}! Как дел+а?\n`;\n\n/*\n{\n  text: 'Здорово! Как дела?',\n  tts: 'Здор+ово! Как дел+а?',\n  end_session: false\n}\n*/\n```\n\n### Пример с параметрами\nДля проброса параметров удобно использовать [`reply`](#reply) вместе со стрелочной функцией:\n```js\nconst { reply, pause, buttons } = require('alice-renderer');\n\nconst welcome = username =\u003e reply`\n  ${['Здравствуйте', 'Добрый день']}, ${username}! ${pause(500)} Как дел+а?\n  ${buttons(['Отлично', 'Супер'])}\n`;\n\nconst response = welcome('Виталий Пот+апов');\n\nconsole.log(response);\n\n/*\n{\n  text: 'Добрый день, Виталий Потапов! Как дела?',\n  tts: 'Добрый день, Виталий Пот+апов! - - - - - - - Как дел+а?',\n  buttons: [\n    {title: 'Отлично', hide: true},\n    {title: 'Супер', hide: true},\n  ],\n  end_session: false\n}\n*/\n```\nПереданные параметры также очищаются от акцентов при записи в поле `text`.\n\n## API\n\n### reply\nОсновная функция библиотеки. \nИспользуется как тег-функция для [template literal](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/template_strings): \n```js\n reply`строка`;\n```\nФормирует ответ для Алисы, раскладывая переданную строку на текст, голос и кнопки. \nПо умолчанию строка записывается одновременно в оба поля `text` и `tts`.\nПрименяя модификаторы можно кастомизировать текстовую и голосовую часть:\n\n```js\nconst { reply, pause, audio, buttons } = require('alice-renderer');\n\nreply`\n  ${audio('sounds-game-win-1')} ${['Привет', 'Здор+ово']}! ${pause(500)} Как дел+а?\n  ${buttons(['Отлично', 'Супер'])}\n`;\n\n/*\n{\n  text: 'Здорово! Как дела?',\n  tts: '\u003cspeaker audio=\"alice-sounds-game-win-1.opus\"\u003e Здор+ово! - - - - - - - Как дел+а?',\n  buttons: [\n    {title: 'Отлично', hide: true},\n    {title: 'Супер', hide: true},\n  ],\n  end_session: false\n}\n*/\n```\n\n### reply.end\nФормирует ответ ровно также, как и [`reply`](#reply), но завершает сессию:\n\n```js\nconst { reply } = require('alice-renderer');\n\nreply.end`До новых встреч!`;\n\n/*\n{\n  text: 'До новых встреч!',\n  tts: 'До новых встреч!',\n  end_session: true\n}\n*/\n```\n\n### buttons(items, [defaults])\nДобавляет в ответ кнопки.  \n**Параметры:**\n  * **items** `{Array\u003cString|Object\u003e}` - тайтлы/описания кнопок.\n  * **defaults** `{?Object}` - дефолтные свойства создаваемых кнопок.\n\nВ простейшем варианте кнопки можно задавать текстом:\n```js\nconst { reply, buttons } = require('alice-renderer');\n\nreply`Хотите продолжить? ${buttons(['Да', 'Нет'])}`;\n\n/*\n{\n  text: 'Хотите продолжить?',\n  tts: 'Хотите продолжить?',\n  buttons: [\n    {title: 'Да', hide: true},\n    {title: 'Нет', hide: true},\n  ],\n  end_session: false\n}\n*/\n```\n\nЕсли нужно изменить тип кнопок, то дополнительно выставляем `defaults`:\n```js\nconst { reply, buttons } = require('alice-renderer');\n\nreply`\n  Хотите продолжить? \n  ${buttons(['Да', 'Нет'], {hide: false})}\n`;\n\n/*\n{\n  text: 'Хотите продолжить?',\n  tts: 'Хотите продолжить?',\n  buttons: [\n    {title: 'Да', hide: false},\n    {title: 'Нет', hide: false},\n  ],\n  end_session: false\n}\n*/\n```\n\nДля полной кастомизации можно задавать кнопки объектами:\n```js\nconst { reply, buttons } = require('alice-renderer');\n\nreply`\n  Хотите продолжить? \n  ${buttons([\n    {title: 'Да', payload: 'yes'},\n    {title: 'Нет', payload: 'no'},\n  ])}\n`;\n\n/*\n{\n  text: 'Хотите продолжить?',\n  tts: 'Хотите продолжить?',\n  buttons: [\n    {title: 'Да', payload: 'yes', hide: true},\n    {title: 'Нет', payload: 'no', hide: true},\n  ],\n  end_session: false\n}\n*/\n```\n\n### audio(name)\nДобавляет звук в голосовой канал.  \n**Параметры:**\n  * **name** `{String}` - название звука из [библиотеки звуков](https://tech.yandex.ru/dialogs/alice/doc/sounds-docpage/).\n\n```js\nconst { reply, audio } = require('alice-renderer');\n\nreply`${audio('sounds-game-win-1')} Ура!`;\n\n/*\n{\n  text: 'Ура!',\n  tts: '\u003cspeaker audio=\"alice-sounds-game-win-1.opus\"\u003e Ура!',\n  end_session: false\n}\n*/\n```\n  \n### effect(name)\nДобавляет голосовой эффект.  \n**Параметры:**\n  * **name** `{String}` - название эффекта из [библиотеки эффектов](https://tech.yandex.ru/dialogs/alice/doc/speech-effects-docpage/).\n\n```js\nconst { reply, effect } = require('alice-renderer');\n\nreply`${effect('hamster')} Я говорю как хомяк`;\n\n/*\n{\n  text: 'Я говорю как хомяк',\n  tts: '\u003cspeaker effect=\"hamster\"\u003e Я говорю как хомяк',\n  end_session: false\n}\n*/\n```\n\n### image(imageId, [options])\nДобавляет изображение BigImage в ответ.\n \n**Параметры:**\n  * **imageId** `{String}` - идентификатор изображения, [загруженного в навык](https://yandex.ru/dev/dialogs/alice/doc/resource-upload-docpage/)\n  * **options.title** `{String}` - заголовок изображения\n  * **options.description** `{String}` - описание изображения\n  * **options.appendDescription** `{String}` - описание изображения, которое будет добавлено при автозаполнении текстом\n  * **options.button** `{Object}` - действие по клику на изображение\n  \nЕсли не указывать `title / description` изображения, то эти поля автоматически заполняются из поля `response.text`.\nЛогика заполнения следующая: сначала заполняется `title`, если длина превышает максимальную длину тайтла (128 символов),\nто заполняется `description`. Если и в `description` не помещается (256 символов) - \nто пробуем заполнить и `title` и `description`, остальное обрезаем.\n\nЕсли изначально указано `title / description` - то они сохраняют исходное значение.\n\nПример с автозаполнением `title`:\n```js\nconst { reply, image } = require('alice-renderer');\n\nreply`\n  Вот моя фотка.\n  ${image('1234567/xxx')}\n`;\n\n/*\n{\n  text: 'Вот моя фотка.',\n  tts: 'Вот моя фотка.',\n  end_session: false,\n  card: {\n    type: 'BigImage',\n    image_id: '1234567/xxx',\n    title: 'Вот моя фотка.',\n  }\n}\n*/\n```\n\nПример с автозаполнением `description`:\n```js\nconst { reply, image } = require('alice-renderer');\n\nreply`\n  Вот моя фотка.\n  ${image('1234567/xxx', { title: 'Заголовок' })}\n`;\n\n/*\n{\n  text: 'Вот моя фотка.',\n  tts: 'Вот моя фотка.',\n  end_session: false,\n  card: {\n    type: 'BigImage',\n    image_id: '1234567/xxx',\n    title: 'Заголовок',\n    description: 'Вот моя фотка.',\n  }\n}\n*/\n```\n\nПример с параметром `appendDescription`. \nНапример, в поле `description` требуется показывать имя автора фото.\nА сам текст под изображением генерится динамически и может иметь разную длину.\nУказав `appendDescription: 'Автор фото: Иван Иванов'`, получим следующее:\n * если текст под изображением поместился в `title`, то в `description` будет просто `Автор фото: Иван Иванов`\n * если текст под изображением не поместился в `title`, то часть его перенесется в `description`\n   и к нему через пробел добавится `Автор фото: Иван Иванов`\n```js\nconst { reply, image } = require('alice-renderer');\n\nreply`\n  ${getLongText()}\n  ${image('1234567/xxx', { appendDescription: 'Автор фото: Иван Иванов' })}\n`;\n\n/*\n{\n  text: 'long text...',\n  tts: 'long text...',\n  end_session: false,\n  card: {\n    type: 'BigImage',\n    image_id: '1234567/xxx',\n    title: 'long text...',\n    description: 'continue of long text... Автор фото: Иван Иванов',\n  }\n}\n*/\n```\n\n### pause([ms])\nДобавляет паузу.  \n**Параметры:**\n  * **ms** `{?Number=500}` - время в милисекундах.\n\n```js\nconst { reply, pause } = require('alice-renderer');\n\nreply`Дайте подумать... ${pause()} Вы правы!`;\n\n/*\n{\n  text: 'Дайте подумать. Вы правы!',\n  tts: 'Дайте подумать. sil \u003c[500]\u003e Вы правы!',\n  end_session: false\n}\n*/\n```\n\n### br([count])\nДобавляет перенос строки в текстовый канал. Вставка `\\n` не подходит,\nт.к. исходные переносы строк вырезаются для удобства записи ответов в backticks `` `...` ``.  \n**Параметры:**\n  * **count** `{?Number=1}` - кол-во переносов строк.\n\n```js\nconst { reply, br } = require('alice-renderer');\n\nreply`\n  Следующий вопрос: ${br()}\n  \"В каком году отменили крепостное право?\"\n`;\n\n/*\n{\n  text: 'Следующий вопрос:\\n\"В каком году отменили крепостное право?\"',\n  tts: 'Следующий вопрос: \"В каком году отменили крепостное право?\"',\n  end_session: false\n}\n*/\n```\n\n### text(value)\nДобавляет строку только в текстовый канал.  \n**Параметры:**\n  * **value** `{String|Array\u003cString\u003e}` - строка текста (или массив строк, из которого будет выбран случайный элемент).\n\nНапример если фраза заканчивается многоточнием, то многоточние `...` лучше добавить только в текст.\nМноготочние в голосе лишь создаст ненужную паузу в конце, из-за которой можно не \"услышать\" весь ответ пользователя.\n```js\nconst { reply, text } = require('alice-renderer');\n\nreply`Жизнь сложная штука${text('...')}`;\n\n/*\n{\n  text: 'Жизнь сложная штука...',\n  tts: 'Жизнь сложная штука',\n  end_session: false\n}\n*/\n```\n\n### tts(value)\nДобавляет строку только в голосовой канал.  \n**Параметры:**\n  * **value** `{String|Array\u003cString\u003e}` - строка для голоса (или массив строк, из которого будет выбран случайный элемент).\n\nПолезно например для выражения эмоций:\n```js\nconst { reply, tts } = require('alice-renderer');\n\nreply`${tts('Йохохо!')} Вы угадали!`;\n\n/*\n{\n  text: 'Вы угадали!',\n  tts: ''Йохохо! Вы угадали!',\n  end_session: false\n}\n*/\n```\n\n### textTts(textValue, ttsValue)\nДобавляет первый аргумент в текстовую часть, а второй - в голосовую.  \n**Параметры:**\n  * **textValue** `{String|Array\u003cString\u003e}` - строка для поля `text`.\n  * **ttsValue** `{String|Array\u003cString\u003e}` - строка для поля `tts`.\n\nЭто полезно при выводе значений предназначенных исключительно для экрана (например email):\n```js\nconst { reply, textTts } = require('alice-renderer');\n\nreply`\n  Вы можете написать нам на емейл${textTts(': user1234@example.com', '. Он на вашем экране.')}\n`;\n\n/*\n{\n  text: 'Вы можете написать нам на емейл: user1234@example.com',\n  tts: 'Вы можете написать нам на емейл. Он на вашем экране.',\n  end_session: false\n}\n*/\n```\n\nИли при вставке emoji:\n* в текст лучше их добавить без знаков препинания (так смотрится лучше)\n* в голос наоборот вставить знак препинания без emoji (тогда Алиса прочитает с правильной интонацией)\n\n```js\nconst { reply, textTts } = require('alice-renderer');\n\nreply`Отлично${textTts(' 👌', '!')} Будет скучно - обращайтесь.`;\n\n/*\n{\n  text: 'Отлично 👌 Будет скучно - обращайтесь.',\n  tts: 'Отлично! Будет скучно - обращайтесь.',\n  end_session: false\n}\n*/\n```\n\n### plural(number, one, two, five)\nПодстановка форм слова для числительных. В строках можно использовать плейсхолдеры `$1`, `$2`, `$5`.  \n**Параметры:**\n  * **number** `{Number}` - число.\n  * **one** `{String}` - строка для `1`.\n  * **two** `{String}` - строка для `2`.\n  * **five** `{String}` - строка для `5`.\n\n```js\nconst { reply, plural } = require('alice-renderer');\n\nconst getResponse = count =\u003e reply`\n  У вас ${plural(count, '$1 правильный ответ', '$2 правильных ответа', '$5 правильных ответов')}\n`;\n\ngetResponse(1); // response.text = \"У вас 1 правильный ответ\"\ngetResponse(2); // response.text = \"У вас 2 правильных ответа\"\ngetResponse(5); // response.text = \"У вас 5 правильных ответов\"\ngetResponse(121); // response.text = \"У вас 121 правильный ответ\"\n```\n\n### enumerate(arr, { separator = ', ', lastSeparator = ' или ' })\nПеречисляет не-пустые значения в строку, добавляя \"или\" перед последним. Это более человеко-привычное перечисление.\n**Параметры:**\n  * **arr** `{array}` - список элементов.\n  * **separator** `{string}` - разделитель элементов, кроме последней пары (по умолчанию `', '`)\n  * **lastSeparator** `{string}` - разделитель для последней пары (по умолчанию `' или '`)\n\n**Возвращает:**\n  * `{string}`\n\nПример:\n```js\nconst { reply, enumerate } = require('alice-renderer');\n\nconst getActions = hasHints =\u003e reply`\n  Вы можете \n  ${enumerate([\n    'ответить', \n    'сдаться', \n    hasHints \u0026\u0026 'взять подсказку',\n  ])}\n`;\n\n// если подсказку можно брать:\ngetActions(true) // =\u003e \"Вы можете ответить, сдаться или взять подсказку\"\n\n// если подсказку нельзя брать:\ngetActions(false) // =\u003e \"Вы можете ответить или сдаться\"\n```\n\n### userify(userId, target)\nПерсонализирует функции рендеринга под конкретного пользователя. \nЭто позволяет избежать повторений при выборе ответов из массива.     \n**Параметры:**\n  * **userId** `{String}` - идентификатор пользователя.\n  * **target** `{Function|Object}` - функция рендеринга или объект, ключи которого - функции рендеринга.\n\n**Возвращает:**\n  * `{Function|Object}` \n\nНапример, без использования `userify`:\n```js\nconst { reply } = require('alice-renderer');\n\nconst replySuccess = () =\u003e reply`\n  ${['Отлично', 'Супер', 'Класс']}! \n  Это правильный ответ!\n`;\n\nreplySuccess();\nreplySuccess();\nreplySuccess();\n\n// может оказаться так:\n// =\u003e \"Супер! Это правильный ответ!\"\n// =\u003e \"Супер! Это правильный ответ!\"\n// =\u003e \"Супер! Это правильный ответ!\"\n```\n\nС использованием `userify` ответы будут гарантированно разные:\n```js\nconst { reply, userify } = require('alice-renderer');\n\nconst replySuccess = () =\u003e reply`\n  ${['Отлично', 'Супер', 'Класс']}!\n  Это правильный ответ!\n`;\n\nconst userReplySuccess = userify(userId, replySuccess);\n\nuserReplySuccess();\nuserReplySuccess();\nuserReplySuccess();\n\n// =\u003e \"Супер! Это правильный ответ!\"\n// =\u003e \"Отлично! Это правильный ответ!\"\n// =\u003e \"Класс! Это правильный ответ!\"\n```\n\nТакже в `userify` можно передать объект - тогда будут обернуты все его методы:\n```js\nconst { reply, userify } = require('alice-renderer');\n\nconst replies = {\n  success: () =\u003e reply`${['Отлично', 'Супер', 'Класс']}! Это правильный ответ!`,\n  fail: () =\u003e reply`${['Нет', 'Неверно']}! Это неправильный ответ!`,\n};\nconst userReplies = userify(userId, replies);\n\nuserReplies.success();\n```  \n\n### select(array)\nПо кругу выбирает случайные элементы из массива, исключая повторения.\nНеявно это используется при выборе элементов из массивов внутри reply.\nНо явный вызов тоже иногда полезен.\nНапример, если нужно давать неповторяющиеся ответы, которые внутри себя содержат вариативность.\n\n**Параметры:**\n  * **array** `{Array}` - массив возможных значений.\n\n**Возвращает:**\n  * `{*}`\n\nПример:\nЕсть два варианта ответа, которые должны чередоваться:\n```js\nconst longAnswer = reply`\n  ${['Отлично', 'Супер', 'Класс']}!\n  Это правильный ответ!\n`;\n\nconst shortAnswer = reply`\n  ${['Верно', 'Точно']}!\n`;\n```\nНа первый взгляд можно просто положить их еще одним массивом в reply:\n```js\nconst answer = reply`\n  ${[ longAnswer, shortAnswer ]}\n`;\n```\nНо это будет работать неправильно, т.к. сами ответы содержат внутри себя вариативность (массивы значений).\nПоэтому ключ для общего массива будет всегда вычисляться разный (это делается через `JSON.stringify()`).\n\nРешить эту проблему можно используя `select()` и вспомогательный массив:\n```js\nconst { reply, userify, select } = require('alice-renderer');\n\nconst replySuccess = () =\u003e {\n  const answerType = select([ 'answer-success-long', 'answer-success-short' ]);\n  if (answerType === 'answer-success-long') {\n    return reply`\n      ${['Отлично', 'Супер', 'Класс']}!\n      Это правильный ответ!\n    `;\n  } else {\n    return reply`\n      ${['Верно', 'Точно']}!\n    `;\n  }\n};\n\nconst userReplySuccess = userify(userId, replySuccess);\n\nuserReplySuccess();\nuserReplySuccess();\nuserReplySuccess();\nuserReplySuccess();\nuserReplySuccess();\n\n// =\u003e \"Супер! Это правильный ответ!\"\n// =\u003e \"Верно!\"\n// =\u003e \"Отлично! Это правильный ответ!\"\n// =\u003e \"Точно!\"\n// =\u003e \"Класс! Это правильный ответ!\"\n```\n\n### once(options, response)\nВозвращает заданный ответ не чаще чем 1 раз в заданное кол-во вызовов или секунд.\n\n**Параметры:**\n  * **options.calls** `{Number}` - вернет response не чаще чем 1 раз в заданное кол-во вызовов.\n  * **options.seconds** `{Number}` - вернет response не чаще чем 1 раз в заданное кол-во секунд.\n  * **options.leading** `{Boolean}` - возвращять ли response при первом вызове. по умолчанию `false`.\n  * **response** `{String|Object}` - ответ\n\n**Возвращает:**\n  * `{String|Object}`\n\nИспользуется только совместно с `userify`.\nНапример, чтобы **раз в 5 вызовов** добавлять `\"Вы классно отвечаете на вопросы!\"`, можно написать так:\n```js\nconst { reply, userify, once } = require('alice-renderer');\n\nconst replySuccess = () =\u003e reply`\n  Правильно!\n  ${once({ calls: 5 }, 'Вы классно отвечаете на вопросы!')}\n`;\nconst userReplySuccess = userify(userId, replySuccess); // важно применить userify, чтобы сохранять вызовы для текущего пользователя\n\n// =\u003e \"Правильно!\"\n// =\u003e \"Правильно!\"\n// =\u003e \"Правильно!\"\n// =\u003e \"Правильно!\"\n// =\u003e \"Правильно! Вы классно отвечаете на вопросы!\"\n// =\u003e \"Правильно!\"\n// =\u003e ...\n```\n\nЛибо чтобы добавлять `\"Вы классно отвечаете на вопросы!\"` не чаще чем **раз в минуту**:\n```js\nreply`\n  Правильно!\n  ${once({ seconds: 60 }, 'Вы классно отвечаете на вопросы!')}\n`;\n```\n\n### configure(options)\nГлобальная конфигурация модуля.  \n**Параметры:**\n  * **options.disableRandom** `{Boolean} = false` - отключает рандом. \n    Все ответы, содержащие массивы, всегда возвращают первый элемент.\n    Это удобно для тестов.\n\n**Возвращает:**\n  * `{undefined}`\n\nНапример:\n```js\nconst { reply, configure } = require('alice-renderer');\n\nconfigure({disableRandom: true});\n\nreply`${['Раз', 'Два', 'Три']}`;\nreply`${['Раз', 'Два', 'Три']}`;\nreply`${['Раз', 'Два', 'Три']}`;\nreply`${['Раз', 'Два', 'Три']}`;\n\n// всегда возвращает {text: \"Раз\", tts: \"Раз\"}\n```\n\n## Рецепты\n\n### Вариативность через массивы\nВариативность в ответах удобно добавлять через массивы, которые выносить в отдельные переменные:\n```js\nconst { reply } = require('alice-renderer');\n\nconst greetingText = [\n  'Привет',\n  'Здор+ово',\n  'Здравствуйте',\n  'Добр+о пожаловать',\n  'Йох+анга',\n];\n\nconst greetingSound = [\n  audio('sounds-game-win-1'),\n  audio('sounds-game-win-2'),\n  audio('sounds-game-win-3'),\n];\n\nconst welcome = () =\u003e reply`\n  ${greetingSound} ${greetingText}! Я голосовой помощник Алиса.\n`;\n```\n\n### Модуль рендеринга ответов\nВсю работу по формированию финального текстового ответа удобно вынести в отдельный модуль. \nЭто позволяет отделить логику навыка от рендеринга и менять эти части независимо:\n\n**replies.js**\n```js\nconst { reply, buttons } = require('alice-renderer');\n\nexports.welcome = () =\u003e reply`\n  Привет! Я голосовой помощник Алиса.\n`;\n\nexports.showMenu = username =\u003e reply`\n  ${username}, вы можете сразу начать игру или узнать подробнее о правилах.\n  ${buttons(['Начать игру', 'Правила'])}\n`;\n\nexports.goodbye = () =\u003e reply.end`\n  Отлично! Будет скучно - обращайтесь.\n`;\n```\n\n**logic.js**\n```js\nconst replies = require('./replies');\n\nfunction handleMenuRequest() {\n  // логика формирования ответа...\n  return replies.showMenu(session.username);\n}\n```\n\n### Обработка условий\nМожно вставлять обработку условных операторов прям в формируемую строку. Falsy значения в ответ не попадут:\n```js\nconst { reply } = require('alice-renderer');\n\nexports.correctAnswer = score =\u003e reply`\n  Правильный ответ!\n  ${score \u003e 100 \u0026\u0026 'Вы набрали больше 100 баллов и получаете приз!'}\n`;\n```\n\nТакже можно использовать вложенный `reply`, если требуется обработка строки в условии:\n```js\nconst { reply, audio } = require('alice-renderer');\n\nexports.correctAnswer = score =\u003e reply`\n  Правильный ответ!\n  ${score \u003e 100 \u0026\u0026 reply`${audio('sounds-game-win-1')}Вы набрали больше 100 баллов и получаете приз!`}\n`;\n```\n\n## Лицензия\nMIT @ [Vitaliy Potapov](https://github.com/vitalets)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvitalets%2Falice-renderer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvitalets%2Falice-renderer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvitalets%2Falice-renderer/lists"}