Jadi INTERCOM'18 kami mati, dengan preferensi dan kasus bisnis. Seperti biasa, pintu masuk ke konferensi dibayar: mereka yang ingin dapat membeli tiket untuk TimePad dengan harga penuh, atau ... mendapatkan diskon dari konsultan reptil
tepat di situs . Tahun lalu, itu berfungsi seperti panggilan balik yang akrab: Anda meninggalkan telepon dalam bentuk khusus, Pavel memanggil Anda sebentar dan mengajukan pertanyaan; semakin banyak jawaban yang benar, semakin tinggi diskon. Kali ini kami memutuskan untuk mengubah mekanika, menjadikannya lebih sulit baik secara teknis maupun masalah. Di bawah cut - Pavlik 2.0 nyali, dengan node dan soket web saat ini, jangan lupa untuk memakai overall sebelum membuka.
Mekanika kontes
Anda pergi ke
intercomconf.com dari browser desktop, di sudut kanan bawah Pavlik "bangun" dalam bentuk obrolan dan menawarkan untuk memainkan permainan. Masukkan nomor, klik "Ini nomor saya" - setelah ini, Pavel mengangkat sesi antara browser Anda dan backend kami.

Jika semuanya berhasil naik dan nomor Anda belum berpartisipasi dalam undian, maka Paul akan menawarkan untuk menelepon 8-800. Di sini awan Voximplant masuk dan kuis dimulai:
Jawab: batas waktu / batas waktu. Berdasarkan meme Ini baik-baik saja .Ya, teka-teki itu seperti ini. Tiga upaya dilakukan untuk setiap pertanyaan: pada awalnya ada gambar "kompleks", kemudian lebih sederhana dan pada akhirnya yang paling sederhana. Upaya pertama memberi poin terbanyak; setelah 5 teka-teki, Pavel menghitung poin dan memberikan tiket gratis atau diskon 10% -30%.
Pada saat yang sama, reptiloid kami cukup pintar: ia memberikan pesan kesalahan (jika Anda salah memasukkan nomor telepon, misalnya), menetapkan bahwa nomor tersebut telah berpartisipasi dalam undian ("Saya melihat nomor yang familier di layar ponsel saya yang tidak ada. Satu upaya di satu tangan - ini adalah aturannya. ") Dan, yang paling penting, mengkorelasikan browser dan cloud. Bagaimana IVR yang berani ini bekerja?
Di rahang kegilaan reptiloid
Jawab: pusat panggilan. Kata Nuff.Singkatnya, Paul 2.0 adalah IVR yang berjalan di cloud kami. Oleh karena itu, semua logika reptiloid harus dijabarkan dalam skrip JS, bukan? Ya, tapi tidak.
Versi kedua Pavel disinkronkan dengan browser klien: di situs Pavel menunjukkan rebuses, dan di telepon mendengar jawaban Anda, tergantung pada gambar mana yang berubah dan hasilnya ditampilkan. Sekilas, interaksi ini dapat diimplementasikan menggunakan
API HTTP kami:
- pertama, browser akan menjalankan skrip menggunakan metode StartScenarios . Dalam respons, metode mengembalikan parameter media_session_access_url dan media_session_access_secure_url yang masing-masing berisi URL untuk HTTP dan HTTPS;
- seseorang dapat berkomunikasi dengan skrip yang sedang berjalan menggunakan URL yang diterima;
- script akan memberi tahu browser gambar mana yang digunakan dan memperbarui skor menggunakan metode httpRequestAsync .
Tetapi bagaimana cara "menangkap" browser khusus? Memang, di
httpRequestAsync Anda harus memberikan URL unik. Dan ya, gambar - mereka juga perlu disimpan di suatu tempat.
Oleh karena itu, selain skrip cloud JS, kami menggunakan backend kami pada
express.js yang dipasangkan dengan
socket.io : ketika pengunjung memasukkan nomor, browser memberikan nomor ini ke backend melalui http, setelah sesi http berubah menjadi sesi di soket web. Akibatnya, skrip terus berkomunikasi dengan backend melalui http, dan backend sudah menggunakan soket web untuk dengan cepat melempar gambar dan menghitung poin ke dalam browser.
Di sisi soket web, backend tampak seperti ini: | '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(); |
| } |
| }) |
Tapi tetap saja, sebagian besar logikanya disimpan dalam skrip. Pertimbangkan reptiloid di sisi ini ...
Ikuti skripnya
Jawab: pembelajaran mesin. Diambil dari Instagram Arnie sendiri .Dari yang sudah jelas: Anda pasti perlu menghubungkan modul pengenalan
ASR .
require(Modules.ASR);
Dari yang menarik:
- skrip memiliki objek pertanyaan dengan semua jawaban dan nama file .jpg ;
setiap kali skrip dijalankan, pertanyaan dikocok menggunakan fungsi pembantu acak :
tampilkan kode | 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; |
| } |
- Penangan "tingkat atas" untuk panggilan masuk ( CallAlerting ) memeriksa keunikan telepon, dan juga berisi penangan untuk menghubungkan dan mengakhiri panggilan. Tepat di dalam onCallConnected ada panggilan ke backend (baca, ke socketio):
tampilkan kode | 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)) |
| }); |
| } |
- tepat di atas startGame terlihat , di dalamnya hanya pertanyaan dicampur, dipotong dan dikirim ke backend bersama dengan indeks gambar:
tampilkan kode | 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 membuat instance ASR dan menunjukkan kamus pengenalan yang disukai. Ketika pemain mengucapkan jawabannya, fungsi tersebut menghentikan ASR dan mulai memproses yang didengar - onRecognitionResult ;
- onRecognitionResult menghapus kelebihan dari jawabannya:
let rr = e[0].replace(" ", "").replace(" ", "").replace(" ", "").replace(" ", ""); rr = rr.replace(/ /g, '');
Dan kemudian mulai menghitung upaya, poin, dan juga menyuarakan komentar di sepanjang jalan:
tampilkan kode | 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); |
| } |
Fungsi ini juga menambah variabel dengan upaya dan nomor pertanyaan untuk beralih ke pertanyaan berikutnya atau mengakhiri permainan; - Fungsi final gameFinished memberikan backend jumlah poin jika seseorang memenangkan kode promosi - ini dapat dilihat di browser dan didengar di telepon, karena Pavlik menyuarakan kemenangan; setelah itu hangup dilakukan.
Daftar umum skrip mendekati 300 baris, bagian paling produktif adalah pemrosesan hasil pengakuan,
onRecognitionResult .
Berbicara fosil
Jawab: Firefox. Kami memiliki segalanya.Pavel adalah dinosaurus, tetapi ia tetap up to date: itu berkembang dari tahun ke tahun dan masih suka bercanda. Kami harap Anda menghargai versi kedua dari reptiloid dan "live" kami, dan dari sudut pandang implementasi. Bagikan pendapat Anda dalam komentar, menjadi sehat dan ingat - Paul mencintaimu!