因此,我们的INTERCOM'18因优先考虑和商业案例而破产。 像往常一样,会议的入场费是有偿的:想要的人可以全价购买TimePad的门票,或者...
在现场从爬虫类顾问那里获得折扣。 去年,它像一个熟悉的回调一样工作:您以一种特殊的形式留下电话,Pavel一分钟打电话给您并询问问题; 答案越正确,折扣越高。 这次我们决定改变机制,从技术上和问题上都变得更加困难。 在切割下-具有当前节点和Web套接字的Pavlik 2.0胆量,请不要忘记在打开之前穿工作服。
竞赛机制
您可以从桌面浏览器访问
intercomconf.com ,在右下角,Pavlik以聊天的形式“醒来”并提供玩游戏的机会。 输入数字,然后单击“这是我的数字”-之后,Pavel会在您的浏览器和我们的后端之间建立会话。

如果一切顺利完成,而您的电话号码尚未参加抽奖,那么保罗将提出致电8-800。 Voximplant云进入此处,测验开始:
答:截止日期/截止日期。 基于模因这很好 。是的,谜题是这样的。 针对每个问题进行了三项尝试:首先是一张“复杂的”图片,然后是更简单的,最后是最简单的。 最初的尝试最多。 经过5个谜题后,帕维尔(Pavel)数了点,并给了他们免费门票或10%-30%的折扣。
同时,我们的retiploid非常聪明:它发出错误消息(例如,如果您输入的电话号码不正确),确定该号码已经参与了绘图(“我在不存在的手机屏幕上看到了一个熟悉的号码。一方面尝试,这些是规则)。 ”),最重要的是,将浏览器和云相关联。 这个大胆的IVR如何运作?
在下巴 精神错乱 单倍体
答:呼叫中心。 纳夫说。简而言之,Paul 2.0是在我们的云中运行的IVR。 因此,必须在JS脚本中阐明所有的复制逻辑,对吗? 是的,但是没有。
Pavel的第二个版本与客户端的浏览器同步:在站点上,Pavel显示重用,在电话中,他听见您的答案,具体取决于图片的更改和显示的结果。 乍一看,可以使用我们的
HTTP API来实现这种交互:
- 首先,浏览器将使用StartScenarios方法运行脚本。 在响应中,该方法返回参数media_session_access_url和media_session_access_secure_url ,它们分别包含 HTTP和HTTPS的URL。
- 可以使用接收到的URL与正在运行的脚本进行通信;
- 该脚本会告诉浏览器要使用哪些图像,并使用httpRequestAsync方法更新分数。
但是如何“捕获”自定义浏览器? 实际上,在
httpRequestAsync中,您需要传递一个唯一的URL。 是的,图片-它们也需要存储在某个地方。
因此,除了云JS脚本之外,我们还使用了与
socket.io配对的
express.js上的后端:当访问者输入该数字时,浏览器会通过http将其提供给后端,然后http会话变成了Web套接字上的会话。 结果,该脚本通过http不断与后端通信,并且后端已经使用了Web套接字将图片和计算出的点快速投到浏览器中。
在Web套接字一侧,后端看起来像这样: | '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(); |
| } |
| }) |
但是,大多数逻辑仍存储在脚本中。 考虑在这方面的一个reptiloid ...
按照脚本
答:机器学习。 取自Instagram的Arnie本人 。显而易见:您肯定需要连接
ASR识别模块。
require(Modules.ASR);
来自有趣的:
- 脚本中有一个带有所有答案和.jpg文件名的问题对象;
每次运行脚本时,都会使用随机播放帮助功能将问题随机播放 :
显示代码 | 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; |
| } |
- 传入呼叫的“顶级”处理程序( CallAlerting )检查电话的唯一性,并且还包含用于连接和结束呼叫的处理程序。 在onCallConnected内部,有一个对后端的调用(读取到socketio):
显示代码 | 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)) |
| }); |
| } |
- 就在startGame上方,它是可见的 ,其中只是问题被混合,剪切并与图片索引一起发送到后端:
显示代码 | 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创建一个ASR实例并指示首选的识别字典。 当播放器说出答案时,该功能将停止ASR并开始处理listen - onRecognitionResult ;
- onRecognitionResult从答案中删除多余的内容:
let rr = e[0].replace(" ", "").replace(" ", "").replace(" ", "").replace(" ", ""); rr = rr.replace(/ /g, '');
然后,它开始统计尝试次数,得分,并在此过程中表达意见:
显示代码 | 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); |
| } |
该函数还通过尝试和问题编号增加变量,以切换到下一个问题或结束游戏; - 如果有人赢得了促销代码,最终的gameFinished函数将为后端提供积分数量-这可以在浏览器中看到并在电话中听到,因为 帕夫利克表示胜利; 挂断之后。
脚本的一般列表接近300行,最大的部分是识别结果的处理,
onRecognitionResult 。
会说话的化石
答:Firefox。 我们拥有一切。帕维尔(Pavel)是恐龙,但他保持最新状态:它年复一年地发展,但仍然喜欢开玩笑。 我们希望您对我们的rettiloid的第二个版本进行实时评估和实施评估。 在评论中分享您的观点,保持健康并记住-保罗爱您!