{"id":15023128,"url":"https://github.com/max36895/universal_bot","last_synced_at":"2025-10-25T21:32:30.941Z","repository":{"id":40577686,"uuid":"258478015","full_name":"max36895/universal_bot","owner":"max36895","description":"Универсальное приложение, позволяющее писать только логику бота. В дальнейшем логику можно использовать для Навыков для Алисы, а также различных ботов.","archived":false,"fork":false,"pushed_at":"2022-09-04T09:43:44.000Z","size":445,"stargazers_count":3,"open_issues_count":1,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-01-31T10:51:15.197Z","etag":null,"topics":["alisa","alisa-skills","bot","marusia-skills","nlu","skills","telegram-bot","viber-bot","vk-bot","vui"],"latest_commit_sha":null,"homepage":"https://www.maxim-m.ru/bot/documentation/index.html","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/max36895.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null}},"created_at":"2020-04-24T10:17:07.000Z","updated_at":"2022-05-01T09:31:32.000Z","dependencies_parsed_at":"2022-08-09T23:40:29.820Z","dependency_job_id":null,"html_url":"https://github.com/max36895/universal_bot","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/max36895%2Funiversal_bot","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/max36895%2Funiversal_bot/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/max36895%2Funiversal_bot/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/max36895%2Funiversal_bot/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/max36895","download_url":"https://codeload.github.com/max36895/universal_bot/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":238212458,"owners_count":19434955,"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":["alisa","alisa-skills","bot","marusia-skills","nlu","skills","telegram-bot","viber-bot","vk-bot","vui"],"created_at":"2024-09-24T19:58:45.497Z","updated_at":"2025-10-25T21:32:25.644Z","avatar_url":"https://github.com/max36895.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"Универсальное приложение для создания навыков и ботов\n=====================================================\n[![PHP Composer](https://github.com/max36895/universal_bot/workflows/PHP%20Composer/badge.svg?branch=master)](https://packagist.org/packages/m-m/u_bot)\n\nУстановка\n---------\nУстановите движок:\n1. Скачать движок из репозитория;\n2. Установить через composer;\n### Установка из репозитория\nДля установки, просто склонируйте git репозиторий, а после подключайте нужные файлы.\n```bash\ngit clone https://github.com/max36895/universal_bot.git\n```\n### Установка через composer\nДля начала установите composer, а после выполните команду:\n```bash\ncomposer require m-m/u_bot\n```\n\nДокументация\n------------\nДокументация: [u_bot](https://www.maxim-m.ru/bot/documentation/index.html). Получить информации о работе приложения можно в [телеграм канале](https://t.me/joinchat/AAAAAFM8AcuniLTwBLuNsw) или [группе](https://t.me/mm_universal_bot).\nТакже можно прочитать [статью, в которой рассказано как создать навык \"Я никогда не\"](https://www.maxim-m.ru/article/sozdanie-navyika-ya-nikogda-ne)\n\nОписание\n--------\nДвижок позволяет создать навык для Яндекс.Алиса, Маруси, Сбер(SmartApp), бота для vk, viber или telegram, с идентичной логикой.\nТипы доступных приложений в дальнейшем будут дополняться.\n\nПри необходимости есть возможность создать приложение со своим типом бота.\nТип приложения устанавливается в `mmApp::$appType`, по умолчанию используется alisa.\n\n## Старт\nДля успешного старта на всех платформах необходимо настроить конфигурацию приложения.\nА именно настроить подключение к бд(если есть необходимость), а также указать все необходимые авторизационные токены для корректной работы с api.\nМассив с настройками и подключению к базе данных и логам выглядит следующим образом:\n```php\n$config = [\n  /**\n   * @var string: Директория, в которую будут записываться логи и ошибки выполнения\n   */\n  'error_log' =\u003e __DIR__ . '/../../logs',\n  /**\n   * @var string: Директория, в которую будут записываться json файлы\n   */\n  'json' =\u003e __DIR__ . '/../../json',\n  /**\n   * @var array: Настройка подключения к базе данных. Актуально если mmApp::$isSaveDb = true\n   */\n  'db' =\u003e [\n      'host' =\u003e null, // Адрес расположения базы данных (localhost, https://example.com)\n      'user' =\u003e null, // Имя пользователя\n      'pass' =\u003e null, // Пароль пользователя\n      'database' =\u003e null, // Название базы данных\n  ]\n];\n```\nДля установки конфигурации передайте данные в приложение следующим образом:\n```php\n$bot-\u003einitConfig($config);\n```\nМассив с параметрами приложения выглядит следующим образом:\n```php\n$param = [\n  /**\n   * @var string|null: Viber токен для отправки сообщений, загрузки изображений и звуков\n   */\n  'viber_token' =\u003e null,\n  /**\n   * @var array|string|null: Имя пользователя, от которого будет отправляться сообщение\n   */\n  'viber_sender' =\u003e null,\n  /**\n   * @var string|null: Telegram токен для отправки сообщений, загрузки изображений и звуков\n   */\n  'telegram_token' =\u003e null,\n\n  /**\n   * @var string|null: Версия Vk api. По умолчанию используется v5.103\n   */\n  'vk_api_version' =\u003e null,\n\n  /**\n   * @var string|null: Код для проверки корректности Vk бота. Необходим для подтверждения бота.\n   */\n  'vk_confirmation_token' =\u003e null,\n\n  /**\n   * @var string|null: Vk Токен для отправки сообщений, загрузки изображений и звуков\n   */\n  'vk_token' =\u003e null,\n\n  /**\n   * @var string|null: Яндекс Токен для загрузки изображений и звуков в навыке\n   */\n  'yandex_token' =\u003e null,\n\n  /**\n   * @var bool: Актуально для Алисы!\n   * Использовать в качестве идентификатора пользователя Id в поле session-\u003euser.\n   * Если true, то для всех пользователей, которые авторизованы в Яндекс будет использоваться один токен, а не разный.\n   */\n  'y_isAuthUser' =\u003e false,\n\n  /**\n   * @var string|null: Идентификатор приложения.\n   * Заполняется автоматически.\n   */\n  'app_id' =\u003e null,\n\n  /**\n   * @var string|null: Идентификатор пользователя.\n   * Заполняется автоматически.\n   */\n  'user_id' =\u003e null,\n  /**\n   * @var string: Текст приветствия\n   */\n  'welcome_text' =\u003e 'Текст приветствия',\n  /**\n   * @var string: Текст помощи\n   */\n  'help_text' =\u003e 'Текст помощи',\n\n  /**\n   * @var array: Обрабатываемые команды.\n   *  - string name: Название команды. Используется для идентификации команд\n   *  - $var array slots: Какие слова активируют команду. (Можно использовать регулярные выражения если установлено свойство is_pattern)\n   *  - $var bool is_pattern: Использовать регулярное выражение или нет. По умолчанию false\n   *\n   * Пример intent с регулярным выражением:\n   * [\n   *  'name' =\u003e 'regex',\n   *  'slots' =\u003e [\n   *      '\\b{_value_}\\b', // Поиск точного совпадения. Например, если _value_ = 'привет', поиск будет осуществляться по точному совпадению. Слово \"приветствую\" в данном случае не будет считаться как точка срабатывания\n   *      '\\b{_value_}[^\\s]+\\b', // Поиск по точному началу. При данной опции слово \"приветствую\" станет точкой срабатывания\n   *      '(\\b{_value_}(|[^\\s]+)\\b)', // Поиск по точному началу или точному совпадению. (Используется по умолчанию)\n   *      '\\b(\\d{3})\\b', // Поиск всех чисел от 100 до 999.\n   *      '{_value_} \\d {_value_}', // Поиск по определенному условию. Например регулярное \"завтра в \\d концерт\", тогда точкой срабатывания станет пользовательский текст, в котором есть вхождение что и в регулярном выражении, где \"\\d\" это любое число.\n   *      '{_value_}', // Поиск любого похожего текста. Похоже на strpos()\n   *      '...' // Поддерживаются любые регулярные выражения. Перед использованием стоит убедиться в их корректности на сайте: (https://regex101.com/)\n   *  ],\n   *  'is_pattern' =\u003e true\n   * ]\n   */\n  'intents' =\u003e [\n      [\n          'name' =\u003e WELCOME_INTENT_NAME, // Название команды приветствия\n          'slots' =\u003e [ // Слова, на которые будет срабатывать приветствие\n              'привет',\n              'здравст'\n          ]\n      ],\n      [\n          'name' =\u003e HELP_INTENT_NAME, // Название команды помощи\n          'slots' =\u003e [ // Слова, на которые будет срабатывать помощь\n              'помощ',\n              'что ты умеешь'\n          ]\n      ],\n  ]\n];\n```\nДля установки параметров передайте данные в приложение следующим образом:\n```php\n$bot-\u003einitParams($param);\n```\nДалее создайте файл входа в приложение, а также класс отвечающий за логику приложения. Он должен быть унаследован от абстрактного класса `BotController`, и имеет следующий вид:\n```php\n/**\n* Класс, содержащий логику приложения.\n* Обязательно должен быть унаследован от класса BotController!\n*/\nclass ExampleController extends MM\\bot\\controller\\BotController\n{\n /**\n * Метод, отвечающий за обработку пользовательских команд\n */\n public function action($intentName): void\n {\n     /**\n     * Какая-то логика приложения\n     * ...\n     */\n }\n}\n```\n\n# Свой тип приложения.\nДля добавления своего типа приложения, установите тип в значение `T_USER_APP`.\nПосле чего передайте в функцию `run` класс, отвечающий за инициализацию и возврат данных. Класс должен быть унаследован от абстрактного класса `TemplateTypeModel`\n## Создание своего типа приложения\n### Логика нового типа приложения\nДля начала необходимо создать класс, отвечающий за инициализацию и отображение результата работы.\nПроще говоря в данном классе происходит получение данных, а также инициализация параметров(Метод `init()`).\nПосле успешной обработки пользовательского запроса, класс должен подготовить ответ в требуемом формате(Метод `getContext()`).\nПример:\n```php\n\u003c?php\n\nuse MM\\bot\\components\\button\\Buttons;\nuse MM\\bot\\controller\\BotController;\nuse MM\\bot\\core\\mmApp;\nuse MM\\bot\\core\\types\\TemplateTypeModel;\n\nrequire_once __DIR__ . '/../Components/UserButton.php';\nrequire_once __DIR__ . '/../Components/UserCard.php';\nrequire_once __DIR__ . '/../Components/UserSound.php';\n\nclass UserApp extends TemplateTypeModel\n{\n    /**\n     * Инициализация параметров\n     *\n     * @param null|string $content\n     * @param BotController $controller\n     * @return bool\n     * @see TemplateTypeModel::init() Смотри тут\n     */\n    public function init(?string $content, BotController \u0026$controller): bool\n    {\n        if ($content) {\n            $content = json_decode($content, true);\n            $this-\u003econtroller = \u0026$controller;\n            $this-\u003econtroller-\u003erequestObject = $content;\n            /**\n             * Инициализация основных параметров приложения\n             */\n            $this-\u003econtroller-\u003euserCommand = $content['data']['text'];\n            $this-\u003econtroller-\u003eoriginalUserCommand = $content['data']['text'];\n\n            $this-\u003econtroller-\u003euserId = 'Идентификатор пользователя. Берется из $content';\n            mmApp::$params['user_id'] = $this-\u003econtroller-\u003euserId;\n            return true;\n        } else {\n            $this-\u003eerror = 'UserApp:init(): Отправлен пустой запрос!';\n        }\n        return false;\n    }\n\n    /**\n     * Отправка ответа пользователю\n     *\n     * @return string\n     * @see TemplateTypeModel::getContext() Смотри тут\n     */\n    public function getContext(): string\n    {\n        // Проверяем отправлять ответ пользователю или нет\n        if ($this-\u003econtroller-\u003eisSend) {\n            /**\n             * Отправляем ответ в нужном формате\n             */\n            $buttonClass = new UserButton();// Класс отвечающий за отображение кнопок. Должен быть унаследован от TemplateButtonTypes\n            /*\n             * Получение кнопок\n             */\n            $buttons = $this-\u003econtroller-\u003ebuttons-\u003egetButtons(Buttons::T_USER_APP_BUTTONS, $buttonClass);\n\n            $cardClass = new UserCard();// Класс отвечающий за отображение карточек. Должен быть унаследован от TemplateCardTypes\n            /*\n             * Получить информацию о карточке\n             */\n            $cards = $this-\u003econtroller-\u003ecard-\u003egetCards($cardClass);\n\n            $soundClass = new UserSound();// Класс отвечающий за отображение звуков. Должен быть унаследован от TemplateSoundTypes\n            /*\n             * Получить все звуки\n             */\n            $sounds = $this-\u003econtroller-\u003esound-\u003egetSounds('', $soundClass);\n        }\n        return 'ok';\n    }\n}\n```\nКласс отвечающий за инициализацию основных параметров приложения.\n1. Устанавливается идентификатор пользователя\n2. Указывается запрос пользователя(Введенные текст)\n3. Другие необходимые параметры\n\nВ методе getContext происходит отображение полученного результата пользователю. Либо отправка ответа непосредственно через api.\nВ случае отображения данных, необходимо привести ответ в текстовый формат, который поддерживает приложение.\n\n#### Второстепенные компоненты\nВсе типы приложений имеют примерно одинаковый интерфейс класса, и должны быть унаследованы от абстрактного класса.\nОсновные используемые компоненты:\n1. Кнопки/клавиатура - Используется для навигации и отображения кнопок\n2. Карточка - Используется для отображения изображений, коллекции из изображений, либо списка\n3. Звуки - Воспроизводимые звуки. Голосовые сообщения, либо простой звук.\n\nПример для отображения кнопок/клавиатуры:\n```php\n\u003c?php\nuse MM\\bot\\components\\button\\types\\TemplateButtonTypes;\n\nclass UserButton extends TemplateButtonTypes\n{\n    /**\n     * Получение массив с кнопками для ответа пользователю\n     *\n     * @return array\n     */\n    public function getButtons(): array\n    {\n        $objects = [];\n        foreach ($this-\u003ebuttons as $button) {\n            /*\n             * Заполняем массив $object нужными данными\n             */\n        }\n        return $objects;\n    }\n}\n```\nПример для отображения карточки:\n```php\n\u003c?php\n\nuse MM\\bot\\components\\button\\Buttons;\nuse MM\\bot\\components\\card\\types\\TemplateCardTypes;\n\nrequire_once __DIR__ . '/UserButton.php';\n\nclass UserCard extends TemplateCardTypes\n{\n    /**\n     * Получение массива для отображения карточки/изображения\n     *\n     * @param bool $isOne True, если отобразить только 1 картинку.\n     * @return array\n     */\n    public function getCard(bool $isOne): array\n    {\n        $object = [];\n        $countImage = count($this-\u003eimages);\n        if ($countImage \u003e 7) {\n            $countImage = 7;\n        }\n        $userButton = new UserButton();\n        if ($countImage) {\n            if ($countImage === 1 || $isOne) {\n                if (!$this-\u003eimages[0]-\u003eimageToken) {\n                    if ($this-\u003eimages[0]-\u003eimageDir) {\n                        $this-\u003eimages[0]-\u003eimageToken = $this-\u003eimages[0]-\u003eimageDir;\n                    }\n                }\n                if ($this-\u003eimages[0]-\u003eimageToken) {\n                    /*\n                     * Заполняем $object необходимыми данными\n                     */\n                    // Получаем возможные кнопки у карточки\n                    $btn = $this-\u003eimages[0]-\u003ebutton-\u003egetButtons(Buttons::T_USER_APP_BUTTONS, $userButton);\n                    if ($btn) {\n                        // Добавляем кнопки к карточке\n                        $object = array_merge($object, $btn[0]);\n                    }\n                }\n            } else {\n                foreach ($this-\u003eimages as $image) {\n                    if (!$image-\u003eimageToken) {\n                        if ($image-\u003eimageDir) {\n                            $image-\u003eimageToken = $image-\u003eimageDir;\n                        }\n                    }\n                    $element = [];\n                    /*\n                     * Заполняем $element необходимыми данными\n                     */\n                    // Получаем возможные кнопки у карточки\n                    $btn = $image-\u003ebutton-\u003egetButtons(Buttons::T_USER_APP_BUTTONS, $userButton);\n                    if ($btn) {\n                        // Добавляем кнопки к карточке\n                        $object = array_merge($object, $btn[0]);\n                    }\n                    $object[] = $element;\n                }\n            }\n        }\n        return $object;\n    }\n}\n```\nПример для воспроизведения звука:\n```php\n\u003c?php\n\nuse MM\\bot\\api\\YandexSpeechKit;\nuse MM\\bot\\components\\sound\\types\\TemplateSoundTypes;\nuse MM\\bot\\components\\standard\\Text;\n\nclass UserSound extends TemplateSoundTypes\n{\n    /**\n     * Возвращаем массив с воспроизводимыми звуками.\n     * В случае если передается параметр text, то можно отправить запрос в Yandex SpeechKit, для преобразования текста в голос\n     *\n     * @param array $sounds Массив звуков\n     * @param string $text Исходный текст\n     * @return array\n     */\n    public function getSounds($sounds, $text = ''): array\n    {\n        if ($sounds \u0026\u0026 is_array($sounds)) {\n            foreach ($sounds as $sound) {\n                if (is_array($sound)) {\n                    if (isset($sound['sounds'], $sound['key'])) {\n                        $sText = Text::getText($sound['sounds']);\n                        /*\n                         * Сохраняем данные в массив, либо отправляем данные через запрос\n                         */\n                    }\n                }\n            }\n        }\n        /*\n         * если есть необходимость для прочтения текста\n         */\n        if ($text) {\n            $speechKit = new YandexSpeechKit();\n            $content = $speechKit-\u003egetTts($text);\n            if ($content) {\n                /*\n                * Сохраняем данные в массив, либо отправляем данные через запрос.\n                 * п.с. В $content находится содержимое файла!\n                */\n            }\n        }\n        return [];\n    }\n}\n```\n\nUnitTest\n--------\nДля запуска unit тестов установите все зависимости через composer\n```bash\ncomposer install\n```\nПосле установки зависимостей запустите unit тесты, использую одну из команд\n```bash\n.\\vendor\\bin\\phpunit \n```\nили\n```bash\nphpunit\n```\n\n# SSL\nДля работы некоторых приложений, необходимо иметь ssl сертификат. Поэтому стоит его получить. Для этого можно воспользоваться acme.\n## Установка acme.sh\n```bash\ncurl https://get.acme.sh | sh\n```\n## Использование и установка сертификата для сайта\n```bash\nacme.sh --issue -d {{domain}} -w {{domain dir}}\n```\n1. domain - Название домена (example.com)\n2. domain dir - Директория, в которой находится сайт\n\n```bash\nacme.sh --install-cert -d {{domain}} --key-file {{key file}} --fullchain-file {{cert file}} --reloadcmd \"service nginx reload\"\n```\n1. domain - Название домена (example.com)\n2. key file - Директория, в которой хранится ключ сертификата\n3. cert file - Директория, в которой сохранится сертификат\n\n## Важно!\nПосле получения сертификата, необходимо перезапустить сервер. Для ngnix - `sudo service nginx reload`\n\n# Ngrok\nИспользуется для локального тестирование навыка. Актуально в том случае, когда разработчику необходимо протестировать работу приложения в локальной сети.\n## Установка\nСмотрите на сайте [ngrok](https://ngrok.com/download)\n## Запуск\n```bash\nngrok http --host-header=rewrite \u003cdomain\u003e:port\n```\n1. domain - локальный адрес сайта. Важно сайт должен быть доступен на машине! (Прописан в hosts)\n2. port - Порт для подключения. Для бесплатного аккаунта нельзя использовать 443 порт\n\nПосле успешного запуска, нужно скопировать полученную ссылку с https, и вставить в консоль разработчика.\n\n# Тестирование Вашего проекта\nПротестировать приложение можно 2 способами:\n1. Через ngrok\n2. Через консоль\n## Тестирование через Ngroc\nДля тестирование через ngrok, нужно скачать программу, и запустить её.\nПосле полученную ссылку с https, вставить в [консоль разработчика](https://dialogs.yandex.ru/developer), и перейти на вкладку тестирования.\nДанное действие актуально только для Алисы. Для других платформ ссылка вставляется в соответствующую консоль разработчика.\n\n## Тестирование в консоли\nДля тестирования используется тот же код что и при запуске. С той лишь разнице, что нужно вызвать метод test вместо run.\n```php\n// Подключение файла с логикой\nrequire_once __DIR__ . '/controller/ExampleController.php';\n\n$bot = new MM\\bot\\core\\Bot(); // Создание класса приложения\n$bot-\u003einitTypeInGet(); // Получение типа приложения через get параметр type\n$bot-\u003einitConfig([]); // Инициализация конфигурации приложения. Подключение к базе данных и прописывание путей к логам и сохраняемым данным.\n$bot-\u003einitParams([]); // Инициализация параметров приложения. Обрабатываемые команды, токены и тд.\n$bot-\u003einitBotController((new AuthController())); // Инициализация логики приложения\necho $bot-\u003etest(); // Запуск приложения\n```\nПосле запустить приложение.\n```bash\nphp index.php\n```\nОткроется консоль с Вашим приложением. Для выхода из режима тестирования нужно:\n1. Если навык в определенный момент ставит `isEnd` в True(Что означает завершение диалога), то нужно дойти до того места сценария, в котором диалог завершается.\n2. Ввести команду exit.\n\nПомимо ответов, можно вернуть время обработки команд.\n\nПомощь и поддержка проекта\n------\nЛюбая помощь и поддержка приветствуется.\nЕсли будут найдены различные ошибки или предложения по улучшению, то смело пишите на почту: maximco36895@yandex.ru\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmax36895%2Funiversal_bot","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmax36895%2Funiversal_bot","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmax36895%2Funiversal_bot/lists"}