Notre INTERCOM'18 s'est donc éteint, avec des préférences et des analyses de rentabilité. Comme d'habitude, l'entrée à la conférence était payante: ceux qui le souhaitaient pouvaient acheter des billets pour TimePad au prix fort, ou ... bénéficiez d'une remise auprès d'un consultant reptilien
directement sur le site . L'année dernière, cela a fonctionné comme un rappel familier: vous laissez le téléphone sous une forme spéciale, Pavel vous appelle dans une minute et pose des questions; plus les réponses sont correctes, plus la remise est élevée. Cette fois, nous avons décidé de changer la mécanique, ce qui la rendait plus difficile à la fois techniquement et en termes de problèmes. Sous la coupe - les boyaux Pavlik 2.0, avec le nœud actuel et les sockets Web, n'oubliez pas de porter une combinaison avant l'ouverture.
Mécanique du concours
Vous visitez
intercomconf.com à partir d'un navigateur de bureau, dans le coin inférieur droit Pavlik se "réveille" sous la forme d'un chat et propose de jouer au jeu. Entrez le numéro, cliquez sur «Voici mon numéro» - après cela, Pavel lève une session entre votre navigateur et notre backend.

Si tout s'est bien passé et que votre numéro n'a pas encore participé au tirage, alors Paul proposera d'appeler le 8-800. Ici, le cloud Voximplant entre et le quiz commence:
Réponse: délai / délai. Basé sur le meme C'est très bien .Oui, les puzzles étaient quelque chose comme ça. Trois tentatives ont été faites pour chaque question: au début, il y avait une image «complexe», puis c'était plus simple et à la fin la plus simple. Les premières tentatives ont donné le plus de points; après 5 énigmes, Pavel a compté des points et a donné un billet gratuit ou une remise de 10% à 30%.
En même temps, notre reptiloïde est assez intelligent: il a émis des messages d'erreur (si vous avez entré le numéro de téléphone de manière incorrecte, par exemple), a déterminé que le numéro avait déjà participé au dessin («Je vois un numéro familier sur l'écran de mon mobile inexistant. Une tentative dans une main - ce sont les règles. ") Et, plus important encore, corrélait le navigateur et le cloud. Comment cette audacieuse IVR a-t-elle fonctionné?
Dans les mâchoires la folie reptiloid
Réponse: centre d'appels. Dit Nuff.Pour mettre les choses au sec, Paul 2.0 est un RVI fonctionnant dans notre cloud. Par conséquent, toute la logique reptiloïde doit être énoncée dans un script JS, non? Oui, mais non.
La deuxième version de Pavel est synchronisée avec le navigateur du client: sur le site Pavel affiche les rébus, et sur le téléphone entend vos réponses, en fonction des changements de photos et du résultat affiché. À première vue, cette interaction pourrait être mise en œuvre à l'aide de notre
API HTTP :
- tout d'abord, le navigateur exécuterait le script à l'aide de la méthode StartScenarios . Dans la réponse, la méthode renvoie les paramètres media_session_access_url et media_session_access_secure_url qui contiennent respectivement les URL pour HTTP et HTTPS;
- on pourrait communiquer avec le script en cours en utilisant les URL reçues;
- le script indiquerait au navigateur les images à utiliser et mettrait à jour le score à l'aide de la méthode httpRequestAsync .
Mais comment «attraper» un navigateur personnalisé? En effet, dans
httpRequestAsync, vous devez passer une URL unique. Et oui, les photos - elles doivent également être stockées quelque part.
Par conséquent, en plus du script cloud JS, nous avons utilisé notre backend sur
express.js associé à
socket.io : lorsque le visiteur a entré le numéro, le navigateur a donné ce numéro au backend via http, après quoi la session http s'est transformée en une session sur les sockets web. En conséquence, le script communiquait constamment avec le backend via http, et déjà le backend utilisait des sockets web pour lancer rapidement des images et des points calculés dans le navigateur.
Du côté des sockets web, le backend ressemblait à ceci: | '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(); |
| } |
| }) |
Mais encore, la plupart de la logique était stockée dans le script. Considérez un reptiloïde de ce côté ...
Suivez le script
Réponse: apprentissage automatique. Tiré d'Instagram Arnie lui-même .De l'évidence: vous devez absolument connecter le module de reconnaissance
ASR .
require(Modules.ASR);
De l'intéressant:
- il y avait un objet questions dans le script avec toutes les réponses et les noms de fichiers .jpg ;
à chaque exécution du script, les questions étaient mélangées à l'aide de la fonction d'aide à la lecture aléatoire :
afficher le code | 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; |
| } |
- Un gestionnaire «de niveau supérieur» pour un appel entrant ( CallAlerting ) vérifie l'unicité du téléphone et contient également des gestionnaires pour la connexion et la fin d'un appel. Juste à l'intérieur de onCallConnected, il y a un appel au backend (lire, à socketio):
afficher le code | 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)) |
| }); |
| } |
- juste au-dessus de startGame est visible , seulement les questions sont mélangées, coupées et envoyées au backend avec les indices d'image:
afficher le code | 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 crée une instance ASR et indique le dictionnaire de reconnaissance préféré. Lorsque le joueur prononce la réponse, la fonction arrête l'ASR et commence à traiter le résultat - onRecognitionResult ;
- onRecognitionResult supprime l'excédent de la réponse:
let rr = e[0].replace(" ", "").replace(" ", "").replace(" ", "").replace(" ", ""); rr = rr.replace(/ /g, '');
Et puis, il commence à compter les tentatives, les points et exprime également des commentaires en cours de route:
afficher le code | 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); |
| } |
La fonction incrémente également les variables avec des tentatives et le numéro de question afin de passer à la question suivante ou de terminer le jeu; - La fonction finale gameFinished donne au backend le nombre de points si une personne gagne un code promotionnel - cela peut être vu dans le navigateur et entendu au téléphone, car Pavlik exprime la victoire; après ce raccrochage est fait.
La liste générale du script approche 300 lignes, la pièce la plus volumineuse est le traitement du résultat de la reconnaissance,
onRecognitionResult .
Parler fossile
Réponse: Firefox. Nous avons tout.Pavel est un dinosaure, mais il se tient au courant: il se développe année après année et aime toujours plaisanter. Nous espérons que vous avez évalué la deuxième version de notre reptiloid en direct et en termes de mise en œuvre. Partagez vos opinions dans les commentaires, soyez en bonne santé et souvenez-vous - Paul vous aime!