Алиса, давай ̶П̶о̶д̶р̶о̶ч̶... Придумаем! Вебсокет, нейросети и автоответчик.
FLEXZ SHIT - https://t.me/f1rockstar
Дело было вечером, делать было нечего, но очередной неофит в деле автоматизации, без всяких на то причин ворвался в мою личку с фразой "- А как работать с вебсокетом?", полностью разрушив ламповое времяпрепровождение с 4к камином на ютубе, блюзом и покером. И дабы такого больше не повторилось, мною было решено написать статью по вебсокетам, а так как писать без контекста не очень и интересно, то повинуясь трендам прикрутим нейросети и автоответчик, который в теории будет сливать всеми "любимых" индусов по нашей ссылочке. Для тех кто работает с BAS - будет приятный бонус в виде большого количества скринов и кусков кода. И так, приступим.
Ох уж эти GPT
Большинство встреченных мною чатботов с нейросетями работали либо по платному api, либо через вебсокет в браузерном чате. Т.к. платный api не для нас, то пойдем по самому простому пути, который работает без банов, впн и виртуальных карт - YaGPT. Как и большинство творений бигов русского айти, криво-косо, но работает. А большего нам и не надо. Встроено сие чудо в Алису, и называется "Давай придумаем". Протыкав 999 кнопок "Начать", скачав яндекс браузер и повторив 999 кликов, попадаем наконец то на страницу с чатом. Включаем дебагер, перезагружаем и видим что то непонятное.
Чтоб понять как это работает, жмякаем по кнопочке с вопросом, ждемс ответа, и пишем что нибудь свое, а после начинаем искать в запросах, что куда ушло, а что куда пришло. жмякаем в дебагере ctrl+f и в путь. Естественно ничего не находим, но видим вебсокет. Ищем наше сообщение внутри запроса иииии бинго! мы нашли запрос который отправил наш вопрос.
Смотрим его содержимое, видим кучу айдишников и наш текст. Выглядит не сложно (на скрине эта тулза)
Ответ в вебсокете всегда содержит id запроса, по этому вбиваем в поиск его, видим в поиске запрос-ответ, анализируем ответ.
Как писал классик - это элементарно, Ватсон. Очищаем поиск, находим первое отправленное сообщение. Это коннект, и если есть какая то защита она почти всегда там. Сбрасываем поиск, смотрим первое сообщением, видим айди и токен. Пробуем найти токен в запросах, и видим его в js скриптах. Радостно, потому что какие либо данные в js это всегда статика, и нам не придется изгаляться в муках реверса, пытаясь понять каким образом они генерируются. Ну а айди.. в подавляющем большинстве случаев айдишники это простой рандом, по этому сперва пробуем его, а пробуем искать генерацию уже после того как попытка рандома потерпит крах. Надеюсь сейчас все пройдет хорошо.
Оно работает, Карл!
Запросы мы нашли, пробуем протестить. Открываем какой нибудь сайтик для проверки вебсокета, например - ТЫК
Копипастим адрес для конекта вебсокета отсюда:
и вставляем вот сюда, жмякая на "Connect"
Видим подключение, радуемся, оно работает. Но радуемся рано, пробуем отправить первое сообщение, заменив айдишники на рандом, в моем случае последние 4 символа изменены на 9999. И так сойдет:) Видим что запрос ушел, а ответ не пришел. Что то непонятное
Смотрим второе сообщение в списке запросов в дебагере. Не видим ничего необычного, но обращаем внимание на
{"external_skill_fixed_activate_semantic_frame":{"fixed_skill_id":{"string_value":"b7c42cab-db61-46ba-871a-b10a6ecf3e0d"}
Мы видим кучу "fixed", что переводится как "фиксированный, постоянный". Верим и не меняем в этом параметре ничего, а остальные айдишники "рандомим"
Пробуем отправить, и получаем наконец то какой то ответ. И надо было обратить внимание что запросы у нас уходят под ключем event, а ответы приходят под directive. А нам тут еще и стрелочку нарисовали, шикарно. Вроде все идет как надо, навык запускается.
Но так как приветственное сообщение от алисы мы еще не получили, пробуем отправить третье сообщение из дебагера. Смотрим, а там... наше фиксированное значение. При попытке его зарандомить была получена ошибка, но оставлю это за кадром.
Как и поступали раньше, заменяем последние 4 символа всех айдишников кроме этого на 9999, и отправляем. Кажется все заработало
Теперь попробуем спросить у нее что нибудь свое. Откручиваем в начало, и смотрим на запрос с собственным текстом еще раз. Втыкать скрин повторно я не буду.
У нас тут явно есть контекст, значит где то в запросах должен быть ключик предыдущего сообщения. с помощью поиска найти его не составит труда и пихать лишний скрин я тоже не буду, а кто будет пробовать сам и не найдет... тут никакая статья не поможет:) Забиваем поля рандомными айдишниками, и пихаем свой вопрос. Попросим ее написать код для подключения к вебсокету на nodejs. И Вот теперь оно работает!
А тем, кто будет работать с этим же инструментом для вебсокетов, то слева можно сохранить свои запросы.
Работай с*ка
Запросики мы разобрали, теперь надо впихнуть это в бас. А так как это должен быть автоответчик, то прикрутим телеграм бота. Проще было бы сделать на чистом nodejs, но мыж не ищем легких путей, по этому бас.
Работы с самим вебсокетом немного, да и подготовка тел к отправке небольшая, прикреплю лишь пару скринов. Для начала общий вид. В самом начале объявляем функции, которые будут использованы чаще всего, а это генерация айдишников и преобразование даты. Заострять внимание не буду, но код тут ТЫК. И не плеваться что прицеплен сюда, самому ж проще будет скопипастить:)
Функции мы объявили, теперь готовим данные для отправки. Все запросы мы разобрали выше, так что хватит и скрина. С оставшимися поступить аналогично. Тут у нас самый последний запрос, который будет содержать промт.
Ну и то, ради чего мы все сегодня тут собрались. А именно, вебсокеты. Для начала немного теории.
Вебсокет - протокол для быстрого обмена данных между клиентом и сервером. Простой запрос и простой ответ, больше в нем нет ничего. Единственная сложность состоит в том, что весь обмен данных происходит в одном соединении. Выглядит примерно так:
Много много маленьких запросов, соединенных в один. К сожалению готового кубика нет, по этому нам нужно будет подключить в BAS Node JS и установить библиотеку WS.
Сам код прост и мал, но в контексте баса нужно понимать одну единственную вещь. Почти любой код на node js нужно помещать под "Синхронизировать", а resolve() должно быть в том месте, где нам нужно получить результат и завершить выполнение кода.
Если его разместить не там где нужно, то мы получим либо бесконечное выполнение кубика и вылет по таймауту, или мгновенное выполнение кубика, который завершит работу, не дождавшись результата. Код все равно будет выполнен, но вот как с этим работать тема для отдельной статьи:)
Конкретно в этом случае, мы устанавливаем соединение и отправляем подряд 4 запроса, затем ждем ответ, проверяя его на наличие результатов промта, и только после его получения завершаем выполнение кубика. Текст кода тут ТЫК
К сожалению, дальше нам нужно разорвать соединение, и чтоб отправить пятое, десятое, двадцатое сообщение, нужно будет открывать его заново. В большинстве случаев это не играет абсолютно никакой роли, ибо от разрыва никто не застрахован и для сервера повторный конект будет выглядеть как попытка переподключения. В общем, ничего страшного, главное сохранить все идентификаторы, чтоб не начать новую сессию.
Кто то скажет "А можно же использовать функции баса чтоб обрабатывать данные и вызывать их из ноды при обработке ответов", на что я отвечу "ты чертовски прав, но это статья для чайников". Может быть даже об усложненном использовании nodejs будет написана статья даже вне конкурса, и меня очень простимулируют "огоньки" во время голосования.
Делаем автоответчик
А вот сейчас мы пойдем простым путем, и прицепим готовый модуль с форума. ТЫК
Описывать процесс создания телеграм бота не буду, будем считать что у нас есть уже id от бота. Пихаем его в "Ждать сообщение" и пихаем проверку на существование переменной с текстом. Нам придет большой и страшный объект, из которого нужно извлечь текст и поместить его в переменную с промтом. Выполняем код с вебсокетом, и отправляем ответ назад, используя кубик "Отправить текст". Туда будет нужен ChatId, который лежит рядом с текстом сообщения и токен бота.
JSON.parse([[LAST_MESSAGE]]).result[0].message.text
Так мы получаем текст сообщения от бота.
JSON.parse([[LAST_MESSAGE]]).result[0].message.chat.id
Не забываем поставить тип переменной expression, или используем модуль JSON
И в результате получаем примерно такой результат.
Возможно временами будет возникать ошибки при отправке сообщения в телегу, но лечится с помощью encodeURI() или кубика "Url компонент кодировать/декодировать"
И на этом все. А для тех кто дочитал, вот сервис ТЫК где можно довольно легко подогнать под адалт потребности нейронку на английском языке почти так же, как описано в статье. Пусть это будет домашним заданием для самых ответственных. Всем удачи и хорошего трафика! Дальше только куски кода.
Объявление функций
var characters = "abcdef0123456789"; // символы, используемые для генерации
var generate_group = function(length) {
for (var i = 0; i < length; i++) {
group += characters[(rand(1,characters.length)-1)];
function format_time(timestamp) {
var date = new Date(timestamp);
var year = date.getFullYear().toString();
var month = ("0" + (date.getMonth() + 1)).slice(-2); // Добавляем ведущий ноль к месяцу
var day = ("0" + date.getDate()).slice(-2); // Добавляем ведущий ноль к дню
var hours = ("0" + date.getHours()).slice(-2); // Добавляем ведущий ноль к часам
var minutes = ("0" + date.getMinutes()).slice(-2); // Добавляем ведущий ноль к минутам
var seconds = ("0" + date.getSeconds()).slice(-2); // Добавляем ведущий ноль к секундам
return year + month + day + "T" + hours + minutes + seconds;
Код вебсокета
const WebSocket = require('ws');
const url = 'wss://uniproxy.alice.ya.ru/uni.ws';
const socket = new WebSocket(url);
await(new Promise((resolve, reject) => {
console.log('Соединение установлено');
socket.send(JSON.stringify([[FIRST]]));
socket.send(JSON.stringify([[SECOND]]));
socket.send(JSON.stringify([[THIRD]]));
socket.send(JSON.stringify([[QUEST_REQUEST]]));
socket.on('message', (data) => {
let answer_text = JSON.parse(data).directive.payload.response.directives[0].payload.text