Portanto, nosso INTERCOM'18 morreu, com preferência e casos de negócios. Como sempre, a entrada para a conferência foi paga: quem quisesse poderia comprar ingressos para o TimePad pelo preço total ou ... obter um desconto de um consultor reptiliano
no site . No ano passado, funcionou como um retorno de chamada familiar: você deixa o telefone de forma especial, Pavel liga para você em um minuto e faz perguntas; quanto mais respostas corretas, maior o desconto. Desta vez, decidimos mudar a mecânica, tornando mais difícil tanto tecnicamente quanto em termos de questões. Sob o corte - as tripas do Pavlik 2.0, com o nó atual e os soquetes da Web, não se esqueça de usar macacão antes de abrir.
Mecânica do concurso
Você acessa o
intercomconf.com em um navegador de desktop, no canto inferior direito, a Pavlik “acorda” na forma de um bate-papo e se oferece para jogar. Digite o número, clique em "Aqui está o meu número" - depois disso, Pavel levanta uma sessão entre o navegador e o back-end.

Se tudo tiver aumentado com sucesso e seu número ainda não tiver participado do sorteio, Paulo oferecerá o número 8-800. Aqui a nuvem Voximplant entra e o questionário começa:
Resposta: prazo / prazo. Baseado no meme Isso é bom .Sim, os quebra-cabeças eram algo assim. Foram feitas três tentativas para cada pergunta: a princípio, havia uma imagem “complexa”, depois era mais simples e, no final, a mais simples. As primeiras tentativas deram mais pontos; após 5 quebra-cabeças, Pavel contou pontos e deu um ingresso grátis ou um desconto de 10% a 30%.
Ao mesmo tempo, nosso reptilóide é inteligente o suficiente: emitiu mensagens de erro (se você digitou o número de telefone incorretamente, por exemplo), determinou que o número já havia participado do desenho (“vejo um número familiar na tela do meu celular inexistente. Uma tentativa em uma mão. Essas são as regras. ") E, o mais importante, correlacionou o navegador e a nuvem. Como funcionou essa ousada URA?
Nos maxilares insanidade reptilóide
Resposta: central de atendimento. Nuff disse.Para colocá-lo seco, Paul 2.0 é um URA em execução em nossa nuvem. Portanto, toda a lógica reptilóide deve ser explicitada em um script JS, certo? Sim mas não
A segunda versão do Pavel é sincronizada com o navegador do cliente: no site, Pavel mostra abusos e o telefone ouve suas respostas, dependendo da alteração das imagens e do resultado exibido. À primeira vista, essa interação pode ser implementada usando nossa
API HTTP :
- primeiro, o navegador executaria o script usando o método StartScenarios . Na resposta, o método retorna os parâmetros media_session_access_url e media_session_access_secure_url que contêm URLs para HTTP e HTTPS, respectivamente;
- pode-se comunicar com o script em execução usando os URLs recebidos;
- o script informa ao navegador quais imagens usar e atualiza a pontuação usando o método httpRequestAsync .
Mas como "pegar" um navegador personalizado? De fato, no
httpRequestAsync você precisa passar um URL exclusivo. E sim, fotos - elas também precisam ser armazenadas em algum lugar.
Portanto, além do script JS da nuvem, usamos nosso backend no
express.js emparelhado com
socket.io : quando o visitante digitou o número, o navegador deu esse número ao back-end via http, após o qual a sessão http se transformou em uma sessão nos soquetes da web. Como resultado, o script se comunicava constantemente com o back-end via http, e o back-end já usava soquetes da web para exibir rapidamente imagens e pontos calculados no navegador.
No lado dos soquetes da web, o backend era assim: | '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(); |
| } |
| }) |
Mas ainda assim, a maior parte da lógica foi armazenada no script. Considere um reptilóide deste lado ...
Siga o script
Resposta: aprendizado de máquina. Retirado do Instagram, o próprio Arnie .Do óbvio: você definitivamente precisa conectar o módulo de reconhecimento
ASR .
require(Modules.ASR);
Do interessante:
- havia um objeto de perguntas no script com todas as respostas e nomes de arquivos .jpg ;
sempre que o script foi executado, as perguntas foram embaralhadas usando a função auxiliar de reprodução aleatória :
mostrar código | 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; |
| } |
- Um manipulador de "nível superior" para uma chamada recebida ( CallAlerting ) verifica a exclusividade do telefone e também contém manipuladores para conectar e encerrar uma chamada. Dentro do onCallConnected há uma chamada para o back-end (leia, para o socketio):
mostrar código | 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)) |
| }); |
| } |
- logo acima do startGame é visível , apenas as perguntas são misturadas, cortadas e enviadas para o back-end junto com os índices de imagem:
mostrar código | 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 |
| } |
| } |
- O startASR cria uma instância ASR e indica o dicionário de reconhecimento preferido. Quando o jogador fala a resposta, a função interrompe o ASR e começa a processar o resultado ouvido no reconhecimento ;
- onRecognitionResult remove o excesso da resposta:
let rr = e[0].replace(" ", "").replace(" ", "").replace(" ", "").replace(" ", ""); rr = rr.replace(/ /g, '');
E então começa a contar tentativas, pontos e também comentários de vozes ao longo do caminho:
mostrar código | 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); |
| } |
A função também incrementa as variáveis com tentativas e o número da pergunta para mudar para a próxima pergunta ou terminar o jogo; - a função final gameFinished fornece ao back-end a quantidade de pontos se uma pessoa ganhou um código promocional - isso pode ser visto no navegador e ouvido no telefone, porque Pavlik dá voz à vitória; depois que o hangup estiver pronto.
A lista geral do script se aproxima de 300 linhas, a parte mais volumosa é o processamento do resultado do reconhecimento, no resultado de reconhecimento.
Falando de fóssil
Resposta: Firefox. Nós temos tudo.Pavel é um dinossauro, mas se mantém atualizado: se desenvolve ano após ano e ainda gosta de brincar. Esperamos que você tenha classificado a segunda versão do nosso reptilóide ao vivo e em termos de implementação. Compartilhe suas opiniões nos comentários, seja saudável e lembre-se - Paul te ama!