Pavel 2.0:JS,带有套接字和电话的node.js上的复制顾问

因此,我们的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_urlmedia_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();
}
})
view raw pavel-backend.js hosted with ❤ by GitHub

但是,大多数逻辑仍存储在脚本中。 考虑在这方面的一个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;
    }
    view raw shuffle.js hosted with ❤ by GitHub
  • 传入呼叫的​​“顶级”处理程序( 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
    }
    }
    view raw startGame.js hosted with ❤ by GitHub
  • 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的第二个版本进行实时评估和实施评估。 在评论中分享您的观点,保持健康并记住-保罗爱您!

Source: https://habr.com/ru/post/zh-CN426417/


All Articles