السجلات التفاعلية "الحية": تصور السجلات في Voximplant Kit



ونحن نواصل تحديث Voximplant عدة مع JointJS . ويسرنا أن نعلن عن ظهور سجلات المكالمات الحية. كم هم على قيد الحياة وما إذا كانت خطيرة للمستخدمين العاديين ، اقرأ تحت الخفض.



في السابق ، كانت تسجيلات المكالمات فقط متاحة للمستخدمين لتحليل المكالمات في Voximplant Kit. بالإضافة إلى الصوت ، أردنا ليس فقط إنشاء سجل نصي ، ولكن أداة أكثر ملاءمة لعرض تفاصيل المكالمة وتحليل الأخطاء. وبما أننا نتعامل مع منتج منخفض الكود / لا يحتوي على كود ، ظهرت فكرة تصور السجلات.



ما هو الملح؟ / مفهوم جديد



جميع نتائج المكالمات متاحة الآن كسلسلة من الكتل التي تم تمريرها ، متحركة بشكل مشابه لوضع العرض. يتم تمييز المسار هنا فقط مقدمًا: يمكنك تتبع كيفية تحرك العميل وفقًا للنص البرمجي بشكل مرئي.





للقيام بذلك ، انتقل إلى علامة التبويب محفوظات المكالمات الصادرة أو الواردة أو إلى تقرير الحملة المحددة وانقر فوق "عرض السجل". ثم سيوضح المحرر ما حدث في المكالمة خطوة بخطوة.





مراقبة



يتحكم في إيقاف / بدء التشغيل (1) إيقاف / استئناف التشغيل ، والعودة / التالي (2) تحريك المستخدم في نقطة بداية إلى بداية الكتلة السابقة / التالية. يمكنك أيضًا النقر على المخطط الزمني لبدء التشغيل من نقطة زمنية محددة ، تمامًا مثل تشغيل أغنية.



إذا تضمن السيناريو تسجيل محادثة ، فسيتم تشغيلها بالتوازي مع التحرك عبر الكتل. يتم تمييز التسجيل الصوتي للمخطط الزمني بلون منفصل (3).





لراحة المستخدم ، تتوفر أيضًا قائمة من الكتل التي تم تمريرها مع الطوابع الزمنية ("السجل"):





المفسد:

في علامة التبويب "سجل" ، نخطط لإظهار تفاصيل الكتل. سيساعدوننا على فهم سبب خروج منفذ معين من الحظر وما إذا كانت هناك أخطاء. على سبيل المثال ، بالنسبة لكتلة التعرف ، سنرى نتائج وأخطاء التعرف.

ستكون الكتل المعقدة مثل DialogFlowConnector ، IVR ، ASR ، إلخ ، ذات أهمية قصوى هنا.




المتغيرات



يتم عرض المتغيرات التي تم تغييرها على اليسار كإخطارات منبثقة وفقًا للتسلسل الزمني. أي ، إذا انتقلنا إلى كتلة "تغيير البيانات" ، ستظهر المتغيرات التي تغيرت هناك. سنبتعد عن ذلك (أكثر من 4 ثوانٍ في المخطط الزمني) - ستختفي المتغيرات حتى نجد أنفسنا مرة أخرى في الفترة التي حدث فيها التغيير:







اختراق الحياة



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



يمكنك لمس السجلات بنفسك في Voximplant Kit .



إذن ، ما الذي بالداخل؟



دعونا نرى كيف يتم تنفيذ السجلات الديناميكية في التعليمات البرمجية. دعنا نقول فقط من Joint JS ، لقد أخذنا فقط الرسوم المتحركة واختيار الكتلة ، كما هو الحال في الوضع التجريبي. الباقي (ما يمكن القيام به على أساس هذا) هو خيالنا.



بالمناسبة، لمعرفة المزيد عن الأجزاء الداخلية من طريقة العرض، وقراءة السابقة لدينا المادة .


نحصل على نقاط زمنية



عندما تذهب لعرض السجل ، يرسل الخادم البيانات ، التي تحتوي على قائمة بجميع الكتل التي تم تمريرها ، وتاريخ ووقت الدخول إليها وقائمة المتغيرات التي تغيرت أثناء المكالمة. بمعنى آخر ، في المقدمة نحصل على مصففين من الكائنات: log_path و log_variables.



أيضًا ، تحتوي استجابة الخادم على رابط إلى الصوت ومدته ، إذا تم تسجيل المحادثة.





بناءً على وقت الدخول إلى الكتل ، نحسب النقاط الزمنية بالمللي ثانية ونكتبها لكل كتلة ومتغيرات. النقطة المرجعية (0 مللي ثانية) - وقت الدخول إلى الكتلة الأولى. لذا ، إذا دخلنا الكتلة الثانية بعد 5 ثوانٍ من بدء المكالمة ، فإن النقطة الزمنية للكتلة الثانية = 5000 مللي ثانية. باستخدام هذه النقاط الزمنية ، نحسب المدة الإجمالية للسجل.



تحديث الجدول الزمني



بعد الضغط على زر التشغيل ، يبدأ المخطط الزمني في التحديث كل 10 مللي ثانية. خلال كل تحديث ، نتحقق مما إذا كان الوقت الحالي يطابق إحدى النقاط الزمنية:



const found = this.timePoints.find((item) => item === this.playTime);


إذا كان هناك تطابق ، فسنبحث عن جميع الكتل التي يكون لها الوقت = الوقت الحالي + 600 مللي ثانية (الوقت الذي تتحرك فيه الرسوم المتحركة بين الكتل).



رمز طريقة UpdatePlayTime ():



updatePlayTime(): void {
    const interval = 10;
    let expected = Date.now() + interval;

    const tick = () => {
        const drift = Date.now() - expected;
        const found = this.timePoints.find((item) => item === this.playTime);
        this.$emit('update', {
            time: this.playTime,
            found: found !== undefined
        });

        if (this.playTime >= this.duration) {
            this.isPlay = false;
            this.playTime = this.duration;
            clearTimeout(this.playInterval);
            this.$emit('end', this.playTime);
            return;
        }

        expected += interval;

        this.playTime += 0.01;
        this.playTime = +this.playTime.toFixed(2);

        this.updateProgress();

        this.playInterval = window.setTimeout(tick, Math.max(0, interval - drift));
    };

    this.playInterval = window.setTimeout(tick, 10);
}


أيضا ، كل 90 مللي ثانية نتحقق من المصادفات للوقت الحالي والنقاط الزمنية للمتغيرات المتغيرة + 4000 مللي ثانية (الوقت الذي يتم خلاله تعليق الإخطار حول تغيير المتغير).



اختيار الكتل



بعد العثور على جميع التطابقات ، أضف كتلًا إلى قائمة الانتظار للاختيار وابدأ الرسوم المتحركة للروابط.



إذا كانت هناك عدة كتل ذات نقطة زمنية = الوقت الحالي + 600 مللي ثانية ، فسيكون الانتقال متحركًا إلى الأخير فقط:



if (i === blocks.length - 1) {
    await this.selectBlock(blocks[i], 600, true, true);
}


هذا ضروري لأن هناك كتل تتم معالجتها بسرعة كبيرة. على سبيل المثال ، "التحقق من البيانات" ، "تغيير البيانات" ، إلخ. - يمكن اجتياز عدة كتل في ثانية واحدة. إذا قمت بتحريكها بالتتابع ، فسيكون هناك وقت زمني متأخر.



كود طريقة OnUpdateTimeline:



async onUpdateTimeline({
    time,
    found
}) {
    this.logTimer = time * 1000; //   
    this.checkHistoryNotify();

    if (!found) return;

    //        + 600
    const blocks = this.callHistory.log_path.filter((item) => {
        return item.timepoint >= this.logTimer && item.timepoint < this.logTimer + 600;
    });

    if (blocks.length) {
        this.editor.unselectAll();

        for (let i = 0; i < blocks.length; i++) {

            if (i === blocks.length - 1) {
                await this.selectBlock(blocks[i], 600, true, true);

                const cell = this.editor.getCellById(blocks[i].idTarget);
                this.editor.select(cell);
            } else {
                await this.selectBlock(blocks[i], 0, false, true);
            }
        }
    }
}


وهكذا في دائرة ، هناك مصادفة - نختار الكتل ، إذا كانت الكتلة موجودة بالفعل في قائمة الانتظار - لا نفعل شيئًا.



تساعدنا طريقة selectBlock () في ذلك:



async selectBlock(voxHistory, timeout = 700, animate = true, animateLink = true) {
    const inQueue = this.selectQueue.find((item) => item[0].targetId === voxHistory.idTarget);

    if (!inQueue) this.selectQueue.push(arguments);

    return this.exeQueue();
}


الترجيع



عند الترجيع ، نفس المبدأ: عندما يتم نقل المخطط الزمني ، نحصل على الوقت الذي نحتاج فيه إلى الترجيع والإزالة من الكتل المحددة بنقاط زمنية أطول من الوقت الحالي:



const forSelect = this.callHistory.log_path.filter((item) => {
        const time = accurate ? item.accurateTime : item.timepoint;
        return time <= this.logTimer;
    });


نقوم بعمل انتقال متحرك لآخر منهم.



كود طريقة OnRewind ():



async onRewind({
    time,
    accurate
}, animation = true) {
    this.editor.unselectAll();
    this.stopLinksAnimation();
    this.checkHistoryNotify(true);

    const forSelect = this.callHistory.log_path.filter((item) => {
        const time = accurate ? item.accurateTime : item.timepoint;
        return time <= this.logTimer;
    });

    for (let i = 0; i < forSelect.length; i++) {
        if (i === forSelect.length - 1) {
            await this.selectBlock(forSelect[i], 600, animation, false);
            const cell = this.editor.getCellById(forSelect[i].idTarget);
            this.editor.select(cell);
        } else {
            await this.selectBlock(forSelect[i], 0, false, false);
        }
    }

    if (this.isPlay) this.restartAnimateLink();

    this.onEndTimeline();
}


تشغيل الصوت



تشغيل / إيقاف الصوت أسهل. إذا تزامن الخط الزمني مع بداية التسجيل ، فإنه يبدأ في اللعب ثم تتم مزامنة الوقت. طريقة updatePlayer () مسؤولة عن هذا:



updatePlayer() {
    if (this.playTime >= this.recordStart && this.player.paused && !this.isEndAudio) {
        this.player.play();
        this.player.currentTime = this.playTime - this.recordStart;
    } else if (this.playTime < this.recordStart && !this.player.paused) {
        this.player.pause();
    }
}


هذا كل شئ! لذلك ، استنادًا إلى أساليب JS المشتركة وإبداع مطورينا ، ظهرت سجلات حية. تأكد من اختبارها بنفسك إذا لم تكن قد قمت بذلك بالفعل :)



رائع إذا كنت تحب سلسلة مقالاتنا حول تحديثات الحوت. سنستمر في مشاركتنا أحدث وأهم شيء!



All Articles