So ist unsere INTERCOM'18 mit Präferenz- und Geschäftsfällen abgestorben. Wie üblich wurde der Eintritt zur Konferenz bezahlt: Wer wollte, konnte Tickets für TimePad zum vollen Preis kaufen oder ... einen Rabatt von einem Reptilienberater
direkt vor Ort erhalten . Letztes Jahr hat es wie ein vertrauter Rückruf funktioniert: Sie verlassen das Telefon in einer speziellen Form, Pavel ruft Sie in einer Minute an und stellt Fragen; Je korrekter die Antworten, desto höher der Rabatt. Dieses Mal haben wir beschlossen, die Mechanik zu ändern, was es sowohl technisch als auch in Bezug auf Probleme schwieriger macht. Unter dem Schnitt - Pavlik 2.0-Eingeweide mit den aktuellen Knoten- und Web-Sockeln vergessen Sie nicht, vor dem Öffnen Overalls zu tragen.
Wettbewerbsmechanik
Sie besuchen
intercomconf.com über einen Desktop-Browser. In der unteren rechten Ecke „wacht“ Pavlik in Form eines Chats auf und bietet an, das Spiel zu spielen. Geben Sie die Nummer ein und klicken Sie auf "Hier ist meine Nummer". Danach hebt Pavel eine Sitzung zwischen Ihrem Browser und unserem Backend auf.

Wenn alles erfolgreich gestiegen ist und Ihre Nummer noch nicht an der Verlosung teilgenommen hat, bietet Paul an, 8-800 anzurufen. Hier tritt die Voximplantatwolke ein und das Quiz beginnt:
Antwort: Frist / Frist. Basierend auf dem Mem Das ist in Ordnung .Ja, die Rätsel waren so ähnlich. Für jede Frage wurden drei Versuche unternommen: Zuerst gab es ein „komplexes“ Bild, dann war es einfacher und am Ende das einfachste. Die ersten Versuche gaben die meisten Punkte; Nach 5 Rätseln zählte Pavel Punkte und gab entweder eine Freikarte oder einen Rabatt von 10% -30%.
Gleichzeitig ist unser Reptiloid klug genug: Es gab Fehlermeldungen aus (wenn Sie beispielsweise die Telefonnummer falsch eingegeben haben), die feststellten, dass die Nummer bereits an der Zeichnung teilgenommen hatte („Ich sehe eine vertraute Nummer auf dem Bildschirm meines nicht vorhandenen Mobiltelefons. Ein Versuch in einer Hand - das sind die Regeln. ") Und vor allem korrelierte der Browser und die Cloud. Wie hat diese gewagte IVR funktioniert?
In den Kiefern Wahnsinn Reptiloid
Antwort: Call Center. Sagte Nuff.Paul 2.0 ist ein IVR, der in unserer Cloud ausgeführt wird. Daher muss die gesamte Reptilienlogik in einem JS-Skript geschrieben werden, oder? Ja, aber nein.
Die zweite Version von Pavel wird mit dem Browser des Kunden synchronisiert: Auf der Website zeigt Pavel Rebusse an und hört auf dem Telefon Ihre Antworten, je nachdem, welche Bilder sich ändern und das Ergebnis angezeigt wird. Auf den ersten Blick könnte diese Interaktion mithilfe unserer
HTTP-API implementiert werden:
- Zunächst würde der Browser das Skript mit der StartScenarios- Methode ausführen . In der Antwort gibt die Methode die Parameter media_session_access_url und media_session_access_secure_url zurück , die die URLs für HTTP bzw. HTTPS enthalten.
- man könnte mit dem laufenden Skript unter Verwendung der empfangenen URLs kommunizieren;
- Das Skript teilt dem Browser mit, welche Bilder verwendet werden sollen, und aktualisiert die Partitur mithilfe der httpRequestAsync- Methode.
Aber wie kann man einen benutzerdefinierten Browser "fangen"? In
httpRequestAsync müssen Sie eine eindeutige URL übergeben. Und ja, Bilder - sie müssen auch irgendwo gespeichert werden.
Daher haben wir zusätzlich zum Cloud-JS-Skript unser Backend für
express.js in Verbindung mit
socket.io verwendet : Als der Besucher die Nummer
eingab , gab der Browser diese Nummer über http an das Backend weiter. Danach wurde die http-Sitzung zu einer Sitzung für Web-Sockets. Infolgedessen kommunizierte das Skript ständig über http mit dem Backend, und bereits das Backend verwendete Web-Sockets, um schnell Bilder und berechnete Punkte in den Browser zu werfen.
Auf der Seite der Web-Sockets sah das Backend folgendermaßen aus: | 'use strict'; |
| const express = require('express'); |
| const request = require('request'); |
| const low = require('lowdb'); |
| const FileSync = 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 = {}; |
| var PORT = process.env.PORT || 3001; |
| |
| app.use(session); |
| |
| io.use(sharedsession(session)); |
| |
| io.on('connection', function (socket) { |
| if (socket[socket.handshake.session.caller_id] === undefined && |
| socket.handshake.session.caller_id !== undefined) { |
| sockets[socket.handshake.session.caller_id] = socket |
| } |
| }); |
| |
| app.use((req, res, next) => { |
| let allowedOrigins = [ |
| // allowed hosts |
| ]; |
| let origin = req.headers.origin; |
| if (allowedOrigins.indexOf(origin) > -1) { |
| res.setHeader('Access-Control-Allow-Origin', origin); |
| } |
| res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept'); |
| res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,PATCH,OPTIONS'); |
| res.header('Access-Control-Allow-Credentials', true); |
| if (req.method === 'OPTIONS') { |
| res.sendStatus(200); |
| } else { |
| next(); |
| } |
| }) |
Trotzdem wurde der größte Teil der Logik im Skript gespeichert. Betrachten Sie ein Reptiloid auf dieser Seite ...
Folgen Sie dem Skript
Antwort: maschinelles Lernen. Entnommen von Instagram Arnie selbst .Aus dem Offensichtlichen: Sie müssen auf jeden Fall das
ASR- Erkennungsmodul anschließen.
require(Modules.ASR);
Aus dem Interessanten:
- Das Skript enthielt ein Fragenobjekt mit allen Antworten und JPG- Dateinamen.
Jedes Mal, wenn das Skript ausgeführt wurde, wurden die Fragen mithilfe der Shuffle- Helfer-Funktion gemischt:
Code anzeigen | function shuffle(a) { |
| var j, x, i; |
| for (i = a.length - 1; i > 0; i--) { |
| j = Math.floor(Math.random() * (i + 1)); |
| x = a[i]; |
| a[i] = a[j]; |
| a[j] = x; |
| } |
| return a; |
| } |
- Ein Handler der obersten Ebene für einen eingehenden Anruf ( CallAlerting ) überprüft das Telefon auf Eindeutigkeit und enthält auch Handler zum Verbinden und Beenden eines Anrufs. Direkt in onCallConnected wird das Backend aufgerufen (read, to socketio):
Code anzeigen | VoxEngine.addEventListener(AppEvents.CallAlerting, async (e) => { |
| call.addEventListener(CallEvents.Connected, onCallConnected); |
| call.addEventListener(CallEvents.Disconnected, onCallDisconnected); |
| // ... |
| }) |
| |
| function onCallConnected(e) { |
| call.say(" , ! : , , <say-as stress='2'></say-as>." + |
| " , . . ??? !", |
| Language.RU_RUSSIAN_MALE); |
| call.addEventListener(CallEvents.PlaybackFinished, startGame); |
| call.record({ |
| stereo: true |
| }); |
| call.addEventListener(CallEvents.RecordStarted, async (rec) => { |
| let res = await Net.httpRequestAsync(ws + '/urlResult?caller_id=' + encodeURIComponent(caller_id) + '&url=' + |
| encodeURIComponent(rec.url)) |
| }); |
| } |
- direkt über startGame ist sichtbar , darin werden nur die Fragen gemischt, geschnitten und zusammen mit den Bildindizes an das Backend gesendet:
Code anzeigen | async function startGame() { |
| call.removeEventListener(CallEvents.PlaybackFinished); |
| shuffle(questions); |
| questions = questions.slice(0, 5); |
| let res = await Net.httpRequestAsync(ws + '/voxResult?caller_id=' + encodeURIComponent(caller_id) + '&data=' + |
| encodeURIComponent(JSON.stringify({ |
| action: "start", |
| // qIndex attempts = 0 |
| data: questions[qIndex].pics[attempts], |
| points: points |
| }))); |
| try { |
| res = JSON.parse(res.text); |
| } catch (err) { |
| Logger.write(err); |
| } |
| if (res.result === true) { |
| Logger.write("===--- The Game has started! ---==="); |
| startASR(); // |
| wireCall(); // ASR |
| } |
| } |
- startASR erstellt eine ASR-Instanz und gibt das bevorzugte Erkennungswörterbuch an. Wenn der Spieler die Antwort spricht, stoppt die Funktion die ASR und beginnt mit der Verarbeitung des gehörten - onRecognitionResult ;
- onRecognitionResult entfernt den Überschuss aus der Antwort:
let rr = e[0].replace(" ", "").replace(" ", "").replace(" ", "").replace(" ", ""); rr = rr.replace(/ /g, '');
Und dann werden Versuche, Punkte und auch Kommentare auf dem Weg gezählt:
Code anzeigen | let found = questions[qIndex].answers.some(r => rr.indexOf(r) >= 0); |
| Logger.write("FOUND: " + found); |
| if (found) { |
| if (attempts == 0) { |
| points += 5; |
| call.say("<say-as stress='3'></say-as>! !", Language.RU_RUSSIAN_MALE); |
| } else if (attempts == 1) { |
| points += 3; |
| call.say("! .", Language.RU_RUSSIAN_MALE); |
| } else if (attempts == 2) { |
| points += 1; |
| call.say(" … . .", Language.RU_RUSSIAN_MALE); |
| } |
Die Funktion erhöht auch die Variablen mit Versuchen und der Fragennummer, um zur nächsten Frage zu wechseln oder das Spiel zu beenden. - Die letzte gameFinished- Funktion gibt dem Backend die Anzahl der Punkte, wenn eine Person einen Aktionscode gewonnen hat - dies kann im Browser gesehen und auf dem Telefon gehört werden, weil Pavlik spricht den Sieg aus; danach ist das Auflegen erledigt.
Die allgemeine Auflistung des Skripts nähert sich 300 Zeilen, das umfangreichste Stück ist die Verarbeitung des Erkennungsergebnisses
onRecognitionResult .
Fossil sprechen
Antwort: Firefox. Wir haben alles.Pavel ist ein Dinosaurier, aber er hält sich auf dem Laufenden: Er entwickelt sich Jahr für Jahr und scherzt immer noch gern. Wir hoffen, Sie haben die zweite Version unseres Reptiloids sowohl live als auch hinsichtlich der Implementierung bewertet. Teilen Sie Ihre Meinung in den Kommentaren mit, seien Sie gesund und denken Sie daran - Paul liebt Sie!