私たちはし続けるとVoximplantキットを更新JointJS。また、通話の「ライブ」ログの表示をお知らせいたします。それらがどれだけ生きているか、そしてそれらが一般ユーザーにとって危険かどうかについては、カットの下をお読みください。
以前は、ユーザーはVoximplant Kitで通話を分析するために通話の録音のみを利用できました。音声に加えて、テキストログだけでなく、通話の詳細を表示してエラーを分析するためのより便利なツールを作成したいと考えました。そして、私たちはローコード/ノーコード製品を扱っているので、ログを視覚化するという考えが現れました。
塩とは/新しいコンセプト
すべての通話結果は、デモモードとの類似によってアニメーション化された、完了したブロックのチェーンとして利用できるようになりました。ここではパスのみが事前に強調表示されています。クライアントがスクリプトに従ってどのように移動したかを視覚的に追跡できます。
これを行うには、発信または着信の履歴タブに移動するか、選択したキャンペーンのレポートに移動して、[ログを表示]をクリックします。そして、エディタは、呼び出しで何が起こったかを段階的に示します。
コントロール
コントロールを停止/開始(1)停止/再開再生、および逆方向/次(2)ユーザーを前/次のブロックの先頭に移動します。曲を再生するのと同じように、タイムラインをクリックするだけで、特定の時点から再生を開始することもできます。
スクリプトに会話の録音が含まれている場合は、ブロックの移動と並行して再生されます。タイムライン上のオーディオ録音は別の色で強調表示されます(3)。
ユーザーの便宜のために、タイムスタンプ(「ログ」)とともに渡されるブロックのリストも利用できます。
スポイラー:
「ログ」タブでは、ブロックの詳細を表示する予定です。これらは、ブロックが特定のポートで終了した理由と、エラーがあったかどうかを理解するのに役立ちます。たとえば、認識単位の場合、結果と認識エラーが表示されます。
ここでは、DialogFlowConnector、IVR、ASRなどの複雑なブロックが最も重要になります。
変数
変更された変数は、年代順に通知ポップアップの形式で左側に表示されます。つまり、「データの変更」ブロックに移動すると、そこで変更された変数がポップアップ表示されます。それから遠ざかってみましょう(タイムラインで4秒以上)-変数は、変更が発生した間隔で再び自分自身が見つかるまで消えます。
ライフハック
スクリプトを変更または削除した後でも、通話ログは元の形式で保持されます。これは、スクリプトロジックの復元に問題がないことを意味します。必要に応じて、いつでもログを参照できます。Voximplantキットで自分
でログに触れることができます。
それで、中身は何ですか?
動的ログがコードにどのように実装されているかを見てみましょう。デモモードのように、Joint JSからアニメーションとブロック選択のみを取得したとしましょう。残り(これに基づいて実行できること)は私たちのファンタジーです。
ちなみに、デモモードの詳細については、前回の記事をご覧ください。
タイムポイントを取得します
ログを表示すると、サーバーはデータを送信します。このデータには、渡されたすべてのブロックのリスト、ブロックを入力した日時、および呼び出し中に変更された変数のリストが含まれます。言い換えると、前部には2つのオブジェクト配列、log_pathとlog_variablesがあります。
また、サーバーの応答には、会話が録音された場合は、音声とその継続時間へのリンクがあります。
ブロックのエントリ時間に基づいて、ミリ秒単位でタイムポイントを計算し、各ブロックと変数についてそれらを記録します。基準点(0 ms)-最初のブロックへのエントリの時間。したがって、コールの開始から5秒後に2番目のブロックに入った場合、2番目のブロックのタイムポイントは5000ミリ秒になります。これらのタイムポイントを使用して、ログの合計期間を計算します。
タイムラインの更新
再生ボタンを押した後、タイムラインは10msごとに更新を開始します。更新のたびに、現在の時刻がいずれかの時点と一致するかどうかを確認します。
const found = this.timePoints.find((item) => item === this.playTime);
一致する場合、timepoint =現在の時間+ 600 ms(アニメーションがブロック間を移動する時間)であるすべてのブロックを探します。
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ミリ秒(変数の変更に関する通知がハングする時間)をチェックします。
ブロックの選択
すべての一致が見つかったら、ブロックをキューに追加して選択し、リンクのアニメーションを開始します。
timepoint =現在の時間+ 600 msのブロックが複数ある場合、遷移は最後のブロックにのみアニメーション化されます。
if (i === blocks.length - 1) {
await this.selectBlock(blocks[i], 600, true, true);
}
非常に速く処理されるブロックがあるので、これは必要です。たとえば、「データの検証」、「データの変更」などです。-1秒で複数のブロックを一度に渡すことができます。それらを順番にアニメーション化すると、タイムラインから遅れが生じます。
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();
}
}
それで全部です!これが、Join JSメソッドと開発者の創造性に基づいたライブログの表示方法です。まだ行っていない場合は、必ず自分でテストしてください。:)
クジラの更新に関する一連の記事が気に入ったら、すばらしいですね。私たちはあなたと最も新鮮で最も興味深いものを共有し続けます!