isamarin / alisa
YandexAlisa PHP Bot
Requires
- php: ^7.1
- ext-curl: *
- ext-json: *
- ext-mbstring: *
- ext-mysqli: *
- ext-openssl: *
- guzzlehttp/guzzle: 6.4.*
- nqxcode/phpmorphy: ^1.0
- oefenweb/damerau-levenshtein: 3.0.*
- dev-master
- 1.0.2
- 1.0.1
- 1.0
- 1.0-RC4
- 1.0-RC3
- 1.0-RC2
- 1.0-RC1
- 0.5.0.1-beta
- 0.5-beta
- 0.4.4.2-beta
- 0.4.4.1-beta
- 0.4.4-beta
- 0.4.3.6-beta
- 0.4.3.5-beta
- 0.4.3.4-beta
- 0.4.3.3-beta
- 0.4.3.2-beta
- 0.4.3.1-beta
- 0.4.3-beta
- 0.4.2-beta
- 0.4.1-beta
- 0.4-beta
- 0.3.10.1-beta
- 0.3.10-beta
- 0.3.9.2-beta
- 0.3.9.1-beta
- 0.3.9-beta
- 0.3.8-beta
- 0.3.7-beta
- 0.3.6-beta
- 0.3.5-beta
- 0.3.4-beta
- 0.3.3-beta
- 0.3.2-beta
- 0.3.1-beta
- 0.3.0-beta
- 0.2.2-beta
- 0.2.1-beta
- 0.2-beta
- 0.1.5-beta
- 0.1.4-beta
- 0.1.3-beta
- 0.1.2-beta
- 0.1.1-beta
This package is auto-updated.
Last update: 2025-03-29 00:58:09 UTC
README
Предварительная настройка
Морфологический словарь
Бот использует преобразование слов в первую форму для более точного распознавания пользовательского запроса. Для корректной работы необходимо иметь словарь слов с нужным языком. Для русского языка подойдет этот набор. В обязательном порядке необходимо указать путь до словаря.
$bot = new Alisa('NAME'); //Такой используется по умолчанию $bot->setDictionaryPath($_SERVER['DOCUMENT_ROOT'] . '/dicts/');
Директория хранения сессий
Сессии – *.json файл в котором хранится вся сервисная информация в рамках сущности одного диалога. Необходимо указать путь до дирректории, в которую будут складироваться данные.
$bot = new Alisa('NAME'); //Такой используется по умолчанию $bot->setDictionaryPath($_SERVER['DOCUMENT_ROOT'] . '/sessions/');
Протокол работы
//Создаем бота. Аргумент – название навыка $bot = new Alisa('myawesomebot'); //Создаем триггер, в качестве аргумента строка с уникальным именем $helloTrigger = new Trigger('HELLO'); //Привязываем к триггеру токены – в данном случае в одну группу. //Токены – ключевые слова, объединенные в группы $helloTrigger->linkTokens(['привет','здравствуйте','приветсвую']); $bayTrigger = new Trigger('bay'); $bayTrigger->linkTokens(['пока','до свидания','прощай']); //Привязываем триггеры боту $bot->addTrigger($helloTrigger,$bayTrigger); //Отправляем ответ, если распознан $helloTrigger $bot->sendResponse($helloTrigger,static function(){ //$answer - экземпляр отправляемого ответа $answer = new Response(); $answer->addText('Привет!'); $answer->addText('Доброго времени суток!'); //обязательно возвращаем объект Response return $answer; ); //Отправляем ответ, если распознан $bayTrigger $bot->sendResponse($bayTrigger,static function(){ $answer = new Response(); $answer->addText('Прошай!'); $answer->addText('Всего доброго'); //обязательно возвращаем объект Response return $answer; );
Стандартные триггеры
По протоколу работы бот обязан иметь как минимум триггер для приветствия, обработку ошибки (вызов помощи).
//Будет вызван автоматически, при первом обращении пользователя к навыку $helloTrigger = new Trigger('HELLO'); $helloTrigger->setAsInit(true); //Будет вызыван автоматически, если ну удалось распознать запрос $mistakeTrigger = new Trigger('MISTAKE'); $mistakeTrigger->setAsMistake(true); //Отправляем ответ, если распознан $mistakeTrigger $bot->sendResponse($mistakeTrigger,static function(){ $answer = new Answer(); $answer->addText('Не удалось понять вашу команду :( '); return $answer; );
В случае, если данные триггеры не определены, бот отошлет ответ по умолчанию со ссылкой на данный пункт. И если это причина, по которой ты читаешь этот текст – шалость удалась.
Триггеры
Триггер
Триггер – команда, на которую должен отреагрировать бот.
Токены
Токены – ключевые слова, или варианты запроса (зависит от выбранного типа распознования)
MORPHY_STRICT
Данный режим использует поочередное сравнение групп токенов, отбирая только подходящие варинты. Разбор идет слева направо – если первая группа не прошла поиск – остальные группы перебираться не будут и бот приступит к просмотру следующего триггера. При первом вхождении всех групп – триггер будет помечен как распознанный, остыльне учавствовать в разборе не будут
$greenTea->linkTokens(['дай','хочу','налей'],['чай'],['зеленый']); $blackTea->linkTokens(['дай','хочу','налей'],['чай'],['черный','индийский']); $coffeTrigger->linkTokens(['дай','хочу','налей'],['кофе']);
При запросе "Налейка, пожалуйста черного чая" сработает $blackTea; При запросе "Сил нет как хочу бодрящего такого кофе" – $coffeTrigger;
Плюсы:
- Хорошо различает очень похожие запросы
- При первом свопадении всех групп токенов, обработка следующих триггеров прекращается
- Очередностью добавления триггеров можно определить те триггеры, которые будут обрабатываться первым. См пункт выше.
- Может вызывать триггер с ошибкой, если команда не была распознана. В данном примере, попросив налить пива, бот скажет что такими полномочиями не обладает.
Минусы:
- Необходимо строго определять важность того или иного ключевого слова. В случае с $greenTea и $blackTea, с аналогичными двумя первыми группами, решающим фактором сыграет третья гурппа токенов – черный чай или все таки чай зеленый.
- И колличество триггеров, и колличеество групп токенов сказывается на скорости работы.
Дамерау-Левенштейн
В отличие от MORPHY_STRICT ключевые слова будут определяться автоматически, но необходимо прописать несколько вариантов запросов. В разборе учавствуют все триггеры, за распознный будет выбираться триггер с наилучшим совпаденеием запроса.
$greenTea->linkTokens(['Налей зеленого чая'],['Хочу зеленого чая'],['Дай зеленый чай']); $blackTeas->linkTokens(['Налей чергого чая'],['Хочу черного чая'],['Дай черный чай']); $coffeTrigger->linkTokens(['Налей кофе'],['Хочу кофе'],['Дай кофе']);
Плюсы:
- Не нужно определять группы ключевых слов. Достаточно прописать только несколько возможных вариантов, которые может запросить пользователь
- Как правило, запрос обрабатывается быстрее, чем аналогичная вариация MORPHY_STRICT
Минусы:
- Обрабатываются все триггеры, для которых прописаны токены. Даже если правильный триггер был выбран первым, остальные триггеры все равно пройдут проверку. Результирующий триггер – триггер с наибольшим совпадением. Соответвено чем больше триггеров, тем медленее работает.
- В отличие от MORPHY_STRICT не может вызывать сообщение с ошибкой распознования. Данный режим всегда выдает какой-либо триггер. В данном примере, попросив налить пива, по какой-то причине бот наливает кофе.
Делегирование
В случаях, когда бот используется для последовательного выполнения команд, можно привязать к триггеру следующий триггер. Например если нужно собрать какую-либо информацию от пользователя, например имя, фамилию итд.
// Декларируем триггеры $nameTrigger = new Trigger('NAME'); $sNameTrigger = new Trigger('SECOND_NAME'); $yoTrigger = new Trigger('YEARS'); $personTrigger = new Trigger('PERSON'); // Назначаем токены для триггера $nameTrigger->setTokens(['давай','хочу','может'],['знакомиться','познакомиться','представлюсь']); // Привязываем следующие триггеры $nameTrigger->nextDelegate($sNameTrigger); $sNameTrigger->nextDelegate($yoTrigger); $yoTrigger->nextDelegate($personTrigger); // Обрабочтик запроса. Сработает если пользователь произнес "Давай познакомимся" $bot->sendResponse($nameTrigger,static function() use ($bot){ $answer = new Response(); $answer->addText('Какое твое имя?'); return $answer; }); // После шага $nameTrigger сработает обработчик $sNameTrigger $bot->sendResponse($sNameTrigger,static function() use ($bot){ $answer = new Response(); $answer->addText('А фамилия?'); return $answer; }); // После шага $sNameTrigger сработает обработчик $yoTrigger $bot->sendResponse($yoTrigger,static function() use ($bot){ $answer = new Response(); $answer->addText('Сколько тебе лет?'); return $answer; });
Для каждого из триггеров нужно создать обработчик с вопросом. При запросе пользователя "Ну давай познакомимся" сработает триггер $nameTrigger. Для триггеров $sNameTrigger и $yoTrigger токены не нужны (в общем то в данном случае они и не смогут сработать, тк пользотваель будет передавать информацию, и в ней нельзя распознать команду), они будут вызваны автоматически друг за другом.
Данные
Триггеры можно использовать не только как способ определения команды пользователя, но и для сбора информации.
//$personTrigger – назначен как следующий триггер после $yoTrigger $bot->sendResponse($personTrigger,static function() use ($bot){ //В качестве аргумента строка, уникальное название триггера $name = $bot->getTriggerData('NAME'); $sName = $bot->getTriggerData('SECOND_NAME'); $yo = $bot->getTriggerData('YEARS'); $answer = new Answer(); $answer->addText("Хорошо {$name} {$sName}, я тебя запомнила, и что тебе {$yo} лет – тоже"); return $answer; );
Бот сохраняет только один экземпляр данных для триггера, таким образом получить можно только последние полученные данные
Ответы
Ответы – это информация, которую бот отсылает пользователю, как только сработал триггер.
Текст и TTS
//$personTrigger – назначен как следующий триггер после $yoTrigger $bot->sendResponse($personTrigger,static function() use ($bot){ $answer = new Answer(); $answer->addText('Один вариант ответа','Од!ин вари!ант отв!ета'); return $answer; );
В методе addText два строчных аргумента. Первый используется для вывода текста, который будет отображен пользователю, второй – тот же самый текст в формате TTS.
Варианты
Для большей интерактивности следует использовать несколько возможных вариантов ответа. Вариант при отправке будет выбран случайно.
$bot->sendResponse($bullshitTrigger,static function() { $answer = new Answer(); $answer->addText('Да не может быть!'); $answer->addText('Чушь собачья!'); $answer->addText('Не верю!'); return $answer; );
Кнопки
Для ответа можно прикрепить кнопку. Это может быть ссылка на веб-страницу или кнопка, при нажатии на которую будет вызван триггер. Кнопок может быть несколько и разных видов.
$bot->sendResponse($helloPerson,static function() { $answer = new Answer(); $answer->addText('Может познакомимся?'); $buttonY = new Button('Давай'); $answer->addButton($button); $buttonN = new Button(); $buttonN->setTitle('Неа'); return $answer; );
В данном примере при нажатии на кнопку будет вызван $nameTrigger из примера в пункте "Делегирование". Таким образом $nameTrigger может быть взыван как голосом пользователя, так и нажатием на кнопку, если пользователь не разговорчив.
Кнопки
Виды кнопок
Бот Алисы может выводить пользователи кнопки в двух разных видах – как кнопка, размещенная под диалогом, так и ссылка внутри отправляемого ответа.
$bButton = new Button('Это кнопка'); //Будет отображена как кнопка, под диалогом. Со следующем ответом отображена не будет, если ее не привязатели к ответу Response $bButton->setHide(true); $bLink = new Button('Это кнопка, но как ссылка') //Будет отображена в ответе. Даже если пользователь выбирал что-то другие, эта ссылка так и останется в сообщении. $bButton->setHide(false);
Базовые возможности
$bButton = new Button('Это кнопка'); * * * // Можно установить заголовок не в конструкторе, а в методе. Можно использовать как способ изменеия заголовка имеющейся кнопки, тем самым создавая вариативность $bButton->setTitle('Это все еще кнопка') // К кнопке можно добавить ссылку. При клике на нее произойдет переход по адресу в браузере. $bButton->addLink('www.SITENAME.DOMAIN'); // Кнопке можно добавить обработчик какого-нибудь тригера. При клике на кнопку бот в первую очередь проверяет есть ли связанный Триггер для кнопки, и если есть – вызывает именно его. $bButton->linkTrigger(Trigger $trigger); //Кнопке можно передать какие-нибудь данные, которые можно будет забрать и использовать, если пользователь нажимал на эту кнопку. $bButton->addPayload(['DATA'=>'SOME_DATA']);
Сохранение данных
Помимо отправленных пользователем данных, которые привязывается и сохраняеются триггером, можно сохранить и свои данные.
$bot->sendResponse($firstRequest,static function() use($bot) { * * * $bot->storeCommonData('Черный чай','TEA'); $bot->getCommonData('TEA'); // Вернет "Черный чай", в т.ч. если пользователь уже перешел к другому запросу * * * );
$bot->sendResponse($secondRequest,static function() use($bot) { * * * $bot->getCommonData('TEA'); // Вернет "Черный чай", в т.ч. если пользователь уже перешел к другому запросу * * * );
Больше возможностей
Зацкиливание триггера
В случаях, когда триггер предполагает не только принятие данных, но и валидацию, следует использовать зацикливание
//Допустим что код триггера $chooseMeds – "MEDS" $bot->sendResponse($chooseMeds, static function () use ($bot) { $answer = new Response(); //Проверяем, это первичный запрос или цикл? if ( ! $bot->isRepeatedRequest()) { //Нет, значит это первичный запрос со стороны пользователя, предлагаем выбор $answer->addText('Какую таблетку ты выберешь, Нео?'); //Задаем циклирование, результат будем проверять в этом же триггере $bot->setRepeat(true); } else { //Неправильный выбор? if ($bot->getTriggerData('MEDS') !== 'Синяя') { $answer->addText('Неверный выбор, Нео'); //Задать цикл $bot->setRepeat(true); } else { //Все ок, выход из триггера $answer->addText('Ты сделал правильный выбор о-о'); } } return $answer; });