Pavel 2.0: Reptiloid consultant on JS, node.js with sockets and telephony
So our INTERCOM'18 has died down, with preference and business cases. As usual, the entrance to the conference was paid: anyone could buy tickets for the TimePad at full price, or ... get a discount from a reptilian consultant directly on the site . Last year it worked like a usual callback: you leave the phone in a special form, Pavel calls you in a minute and asks questions; the more correct answers, the higher the discount. This time we decided to change the mechanics, making it more difficult both technically and in terms of issues. Under the cut - Pavlik 2.0 guts, with the current node and web sockets, do not forget to wear workwear before opening.
Competition mechanics
You enter intercomconf.com from a desktop browser, in the lower right corner Pavlik “wakes up” in the form of a chat and offers to play the game. Enter the number, click "Here is my number" - after that Pavel raises the session between your browser and our backend.
')
If everything has successfully risen and your number has not yet participated in the drawing, then Pavel will offer to call 8-800. Here the Voximplant cloud is already entering and the quiz starts:
Answer: deadline / deadline.Based on the meme This is fine .
Yes, the puzzles were like this. Three attempts were made for each question: first there was a “complex” picture, then simpler and at the end the simplest one. The first attempts gave the most points; after 5 puzzles, Pavel counted up his points and either gave a free ticket or a 10% -30% discount.
At the same time, our reptiloid is smart enough: it showed error messages (if you entered the phone number incorrectly, for example), determined that the number was already participating in the drawing (“I see the familiar number on the screen of my non-existing mobile phone. One attempt in one hand - these are the rules. ") And, most importantly, correlated the browser and the cloud. How did this bold IVR work?
In the mouth madness reptiloid
Answer: call center.Nuff said.
Speaking dryly, Paul 2.0 is an IVR running in our cloud. Therefore, all reptiloid logic should be spelled out in a JS script, right? Yes, but no.
The second version of Pavel is synchronized with the client’s browser: Pavel shows the rebuses on the website, and he hears your answers on the phone, depending on which pictures are replaced and the result is displayed. At first glance, this interaction could be implemented using our HTTP API :
first, the browser would launch the script using the StartScenarios method. In the response, the method gives the parameters media_session_access_url and media_session_access_secure_url which contain the URLs for HTTP and HTTPS, respectively;
the running script could be communicated using the obtained URLs;
the script would tell the browser which pictures to use and update the score using the httpRequestAsync method.
But how to "catch" a custom browser? After all, in the httpRequestAsync, you must pass a unique URL. And yes, pictures - they also need to be stored somewhere.
Therefore, in addition to the cloud-based JS script, we used our backend on express.js paired with socket.io : when the visitor entered the number, the browser gave this number to the backend via http, after which the http session turned into a session on web sockets. As a result, the script constantly communicated with the backend via http, and already the backend used web sockets to quickly transfer images and points to the browser.
In part of the web sockets, the backend looked like this:
'use strict';
constexpress=require('express');
constrequest=require('request');
constlow=require('lowdb');
constFileSync=require('lowdb/adapters/FileSync');
var app =express();
var http =require('http');
var server =http.createServer(app);
var io =require('socket.io')(http).listen(server);
var session =require('express-session')({
secret:'secret',
resave:true,
saveUninitialized:true
});
var sharedsession =require('express-socket.io-session');
var sockets = {};
varPORT=process.env.PORT||3001;
app.use(session);
io.use(sharedsession(session));
io.on('connection', function (socket) {
if (socket[socket.handshake.session.caller_id] ===undefined&&
But still, most of the logic was stored in the script. Consider a reptiloid from this side ...
We go on the script
Answer: machine learning / machine learning.Taken from the Instagram of Arnie himself .
From the obvious: be sure to connect the ASR recognition module.
require(Modules.ASR);
From the interesting:
there was an questions object in the script with all the answers and .jpg file names; each time the script was run, questions were mixed using the shuffle helper function:
A “top-level” handler for an incoming call ( CallAlerting ) checks the phone for uniqueness, and also contains handlers for connecting and ending a call. Just inside onCallConnected there is an appeal to the backend (read, to socketio):
startASR creates an ASR instance and specifies the preferred recognition dictionary. When the player pronounces the answer, the function stops the ASR and starts processing what they hear - onRecognitionResult ;
onRecognitionResult removes unnecessary from the answer:
Also, the function increments variables with attempts and a question number to switch to the next question or end the game;
The final gameFinished function gives backend a sum of points, if a person has won a promotional code - this can be seen in the browser and heard on the phone, since Pavlik voiced winnings; after that hangup is done.
The overall listing of the script approaches 300 lines, the most voluminous piece is the processing of the recognition result, onRecognitionResult .
Speaking fossil
Answer: Firefox.We have everything.
Although Paul and the dinosaur, but still keeps up with the times: it develops from year to year and still loves to joke. We hope you appreciate the second version of our reptiloid and "live", and from the point of view of implementation. Share your views in the comments, be healthy and remember - Paul loves you!