Pavel 2.0: استشاري زائف على JS ، node.js مع مآخذ وهاتف

لذا فقد انتهى إنتركوم 18 الخاص بنا ، مع التفضيلات وحالات العمل. كالعادة ، تم دفع مدخل المؤتمر: أولئك الذين أرادوا شراء تذاكر TimePad بالسعر الكامل ، أو ... الحصول على خصم من مستشار الزواحف مباشرة على الموقع . في العام الماضي ، عملت كرد اتصال مألوف: تترك الهاتف في شكل خاص ، ويتصل بك بافل في دقيقة ويسأل أسئلة ؛ كلما زادت الإجابات الصحيحة ، زاد الخصم. هذه المرة قررنا تغيير الميكانيكا ، مما يجعل الأمر أكثر صعوبة من الناحية الفنية ومن حيث القضايا. تحت القطع - أحشاء Pavlik 2.0 ، مع العقدة الحالية ومآخذ الويب ، لا تنس ارتداء ملابس العمل قبل الفتح.


ميكانيكا المسابقة


تذهب إلى intercomconf.com من متصفح سطح المكتب ، في الزاوية اليمنى السفلية "يستيقظ" Pavlik في شكل محادثة ويقدم عروض للعب اللعبة. أدخل الرقم ، انقر فوق "هنا هو رقمي" - بعد ذلك ، يرفع Pavel جلسة بين المستعرض والواجهة الخلفية.



إذا ارتفع كل شيء بنجاح ولم يشارك رقمك في السحب بعد ، فسيعرض بول الاتصال على الرقم 8-800. هنا تدخل سحابة Voximplant ويبدأ الاختبار:


الجواب: الموعد النهائي / الموعد النهائي. استناداً إلى ميمي هذا جيد .

نعم ، كانت الألغاز شيء من هذا القبيل. تم إجراء ثلاث محاولات لكل سؤال: في البداية كانت هناك صورة "معقدة" ، ثم كانت أبسط وفي النهاية أبسط. أعطت المحاولات الأولى أكبر عدد من النقاط ؛ بعد 5 الألغاز ، أحسب بافيل النقاط وأعطى تذكرة مجانية أو خصم 10 ٪ -30 ٪.

في نفس الوقت ، لدينا reptiloid ذكي بما فيه الكفاية: أصدر رسائل خطأ (إذا قمت بإدخال رقم الهاتف بشكل غير صحيح ، على سبيل المثال) ، قرر أن الرقم قد شارك بالفعل في الرسم ("أرى رقمًا مألوفًا على شاشة هاتفي المحمول غير موجود. محاولة واحدة في يد واحدة - هذه هي القواعد. ") والأهم من ذلك ، ربط المستعرض والسحابة. كيف عمل هذا الجرأة في الرد الآلي؟

في الفكين جنون الزواحف



الجواب: مركز الاتصال. قال ناف.

لتجفيفها ، فإن Paul 2.0 عبارة عن جهاز IVR يعمل في سحابتنا. لذلك ، يجب توضيح كل منطق الزواحف في نص JS النصي ، أليس كذلك؟ نعم ولكن لا.

تتم مزامنة الإصدار الثاني من Pavel مع متصفح العميل: على الموقع يعرض Pavel عمليات إعادة التشغيل ، وعلى الهاتف يسمع إجاباتك ، اعتمادًا على تغيير الصور وعرض النتيجة. للوهلة الأولى ، يمكن تنفيذ هذا التفاعل باستخدام HTTP API الخاص بنا:

  • أولاً ، سيقوم المتصفح بتشغيل البرنامج النصي باستخدام طريقة StartScenarios . في الرد ، تُرجع الطريقة المعلمات media_session_access_url و media_session_access_secure_url التي تحتوي على عناوين URL لـ HTTP و HTTPS ، على التوالي ؛
  • يمكن التواصل مع النص البرمجي الجاري تشغيله باستخدام عناوين URL المستلمة ؛
  • سيقوم البرنامج النصي بإخبار المتصفح بالصور التي سيتم استخدامها وتحديث النتيجة باستخدام طريقة httpRequestAsync .

ولكن كيف "يمسك" متصفح مخصص؟ في الواقع ، في httpRequestAsync تحتاج إلى تمرير عنوان URL فريد. ونعم ، الصور - يجب أيضًا تخزينها في مكان ما.

لذلك ، بالإضافة إلى نص JS النصي ، استخدمنا الواجهة الخلفية على express.js مقترنة بـ socket.io : عندما أدخل الزائر الرقم ، أعطى المتصفح هذا الرقم للواجهة الخلفية عبر http ، وبعد ذلك تحولت جلسة http إلى جلسة على مآخذ توصيل الويب. ونتيجة لذلك ، تواصل البرنامج النصي باستمرار مع الواجهة الخلفية عبر http ، واستخدمت الواجهة الخلفية مآخذ الويب بالفعل لرمي الصور والنقاط المحسوبة بسرعة في المتصفح.

على جانب مآخذ الويب ، بدت الخلفية كما يلي:
'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

ولكن مع ذلك ، تم تخزين معظم المنطق في البرنامج النصي. فكر في الزواحف في هذا الجانب ...

اتبع النص



الجواب: التعلم الآلي. مأخوذة من 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))
    });
    }
  • تظهر فقط بداية اللعبة ، حيث يتم فقط خلط الأسئلة ، قطعها وإرسالها إلى الواجهة الخلفية مع مؤشرات الصورة:

    اظهر الكود
    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 وتبدأ في معالجة السمع - 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 .

الحديث الأحفوري



الجواب: فايرفوكس. لدينا كل شيء.

بافيل هو ديناصور ، لكنه يبقى محدثًا: يتطور عامًا بعد عام ولا يزال يحب المزاح. نأمل أن تكون قد صنفت الإصدار الثاني من زاحفنا الحي من حيث التنفيذ. شارك بآرائك في التعليقات ، وكن بصحة جيدة وتذكر - بول يحبك!

Source: https://habr.com/ru/post/ar426417/


All Articles