转接呼叫时,呼叫的生命周期会抛出 5 个不同的 cdr 事件。队列中的一个充当入站呼叫。
this.ami.on('cdr', async (evt) => {
if (evt.source == '//somerandomtestphonenumber//') {
console.log(evt);
}
var destination: string = evt.destination.toString();
var destinationcontext: string = evt.destinationcontext.toString();
var disposition: string = evt.disposition.toString();
var channel: string = evt.channel.toString();
var destinationchannel: string = evt.destinationchannel.toString();
var source: string = evt.source.toString();
const landline: string = '+97715970084';
var receiver: string = evt.destination.toString();
var startTime: Date = new Date(evt.starttime);
var answerTime: Date =
evt.answertime == '' ? null : new Date(evt.answertime);
var endTime: Date = new Date(evt.endtime);
var totalTime: number = parseFloat(evt.duration);
var callTime: number = parseFloat(evt.billableseconds);
var holdTime: number = totalTime - callTime;
var lastapplication = evt.lastapplication.toString();
var duration: string = evt.duration.toString();
var billableseconds: string = evt.billableseconds.toString();
var uniqueId: string = evt.uniqueid.toString();
// Voicemail
if (destination == 's' && evt.lastapplication == 'VoiceMail') {
const contextFirstPart = destinationcontext.substring(
destinationcontext.indexOf('-') + 1,
destinationcontext.length,
);
const queue = contextFirstPart.substring(
contextFirstPart.indexOf('-') + 1,
contextFirstPart.length,
);
// Note : Event is handled in voicemail service
this.eventEmitter.emit(
'voicemail-received',
new VoicemailLogDto(
queue,
source,
startTime,
answerTime,
endTime,
duration,
disposition,
uniqueId,
channel,
),
);
}
if (
!destinationcontext.includes('ivr') &&
!disposition.includes('CONGESTION') &&
!disposition.includes('FAILED')
) {
var caller: string =
source == landline
? evt.channel.split('/')[1].split('-')[0]
: evt.source.toString();
const callLogEntry = new CreateCalllogDto(
caller,
receiver,
startTime,
answerTime,
endTime,
evt.endtime,
holdTime,
totalTime,
callTime,
disposition,
'unknown',
uniqueId,
);
// For Outbound
if (source == landline && destinationcontext == 'from-internal') {
callLogEntry.type = 'outbound';
if (process.env.NODE_ENV !== 'development') {
await this.callLogService.create(callLogEntry);
}
if (
callLogEntry.disposition == 'NO ANSWER' ||
callLogEntry.disposition == 'BUSY'
) {
this.realtimeGateway.emitData(
'outbound-missed',
'outbound-missed',
[Role.ADMIN, Role.SUPERVISOR, Role.AGENT],
);
}
if (callLogEntry.disposition == 'ANSWERED') {
this.realtimeGateway.emitData(
'outbound-success',
'outbound-success',
[Role.ADMIN, Role.SUPERVISOR, Role.AGENT],
);
}
}
// For inbound
if (source != landline && destinationcontext == 'from-internal') {
callLogEntry.type = 'inbound';
var result = await this.callLogService.create(callLogEntry);
}
// For internal
if (destinationchannel.includes('from-internal-xfer')) {
callLogEntry.type = 'internal';
await this.callLogService.create(callLogEntry);
}
// For queue
if (source != landline && destinationcontext == 'ext-queues') {
const queueLogEntry = new CreateQueueReceiveLogDto(
caller,
destinationchannel.substring(
destinationchannel.indexOf('/') + 1,
destinationchannel.indexOf('@'),
),
receiver,
'ANSWERED',
uniqueId,
evt.endtime,
);
if (process.env.NODE_ENV !== 'development') {
await this.queueLogService.createQueueReceiveLog(queueLogEntry);
}
}
}
});
}
这里,如果有正常呼叫(无分机),则该呼叫将充当入站呼叫,并且仅创建一次呼叫日志报告(抛出 2 个 cdr 事件,1 个用于队列,1 个用于入站) 但是,如果当前正在进行的呼叫中分机之间存在任何呼叫转移,则会有 5 个不同的 cdr 事件,其中 1 个用于队列,4 个事件用于入站,并且会为单个呼叫创建 4 个不同的日志报告。 如果呼叫生命周期中发生任何转接,是否有可能仅创建 1 个呼叫日志报告?
带有传输的 cdr 事件的 console.log(evt) 记录为:
{
event: 'Cdr',
privilege: 'cdr,all',
accountcode: '',
source: '/somerandomphonenumber/',
destination: '4043',
destinationcontext: 'from-internal',
callerid: '"/somerandomphonenumber/" </somerandomphonenumber/>',
channel: 'Local/4043@from-internal-0000032a;2',
destinationchannel: 'SIP/4043-00000835',
lastapplication: 'Dial',
lastdata: 'SIP/4043,,trM(auto-blkvm)I',
starttime: '2023-10-03 11:52:49',
answertime: '2023-10-03 11:52:52',
endtime: '2023-10-03 11:52:57',
duration: '7',
billableseconds: '4',
disposition: 'ANSWERED',
amaflags: 'DOCUMENTATION',
uniqueid: '1696313269.25353',
userfield: ''
}
{
event: 'Cdr',
privilege: 'cdr,all',
accountcode: '',
source: '/somerandomphonenumber/',
destination: '4042',
destinationcontext: 'from-internal',
callerid: '"/somerandomphonenumber/" </somerandomphonenumber/>',
channel: 'Local/4042@from-queue-00000327;2',
destinationchannel: 'SIP/4042-0000082f',
lastapplication: 'Dial',
lastdata: 'SIP/4042,,trM(auto-blkvm)',
starttime: '2023-10-03 11:52:30',
answertime: '2023-10-03 11:52:36',
endtime: '2023-10-03 11:52:49',
duration: '18',
billableseconds: '12',
disposition: 'ANSWERED',
amaflags: 'DOCUMENTATION',
uniqueid: '1696313250.25287',
userfield: ''
}
{
event: 'Cdr',
privilege: 'cdr,all',
accountcode: '',
source: '/somerandomphonenumber/',
destination: '4042',
destinationcontext: 'from-internal',
callerid: '"/somerandomphonenumber/" </somerandomphonenumber/>',
channel: 'Local/4042@from-queue-00000327;2',
destinationchannel: 'Local/4043@from-internal-0000032a;1',
lastapplication: 'Dial',
lastdata: 'SIP/4042,,trM(auto-blkvm)',
starttime: '2023-10-03 11:52:49',
answertime: '2023-10-03 11:52:49',
endtime: '2023-10-03 11:52:57',
duration: '7',
billableseconds: '7',
disposition: 'ANSWERED',
amaflags: 'DOCUMENTATION',
uniqueid: '1696313250.25287',
userfield: ''
}
{
event: 'Cdr',
privilege: 'cdr,all',
accountcode: '',
source: '/somerandomphonenumber/',
destination: '4042',
destinationcontext: 'from-internal',
callerid: '"/somerandomphonenumber/" </somerandomphonenumber/>',
channel: 'Local/4042@from-queue-00000327;2',
destinationchannel: 'SIP/4043-00000835',
lastapplication: 'Dial',
lastdata: 'SIP/4042,,trM(auto-blkvm)',
starttime: '2023-10-03 11:52:57',
answertime: '2023-10-03 11:52:57',
endtime: '2023-10-03 11:53:02',
duration: '4',
billableseconds: '4',
disposition: 'ANSWERED',
amaflags: 'DOCUMENTATION',
uniqueid: '1696313250.25287',
userfield: ''
}
{
event: 'Cdr',
privilege: 'cdr,all',
accountcode: '',
source: '/somerandomphonenumber/',
destination: '602',
destinationcontext: 'ext-queues',
callerid: '"/somerandomphonenumber/" </somerandomphonenumber/>',
channel: 'SIP/ntc_sip-0000082d',
destinationchannel: 'Local/4042@from-queue-00000327;1',
lastapplication: 'Queue',
lastdata: '602,t,,,10,,,,,',
starttime: '2023-10-03 11:52:27',
answertime: '2023-10-03 11:52:27',
endtime: '2023-10-03 11:53:02',
duration: '34',
billableseconds: '34',
disposition: 'ANSWERED',
amaflags: 'DOCUMENTATION',
uniqueid: '1696313247.25273',
userfield: ''
}
了解您已经了解 PBX 上的拨号计划是如何组织的。
Asterisk 存储的 CDR 记录不是针对每个呼叫,而是针对每个使用的频道。除了它消除了该方案中的传出通道。
因此,如果您使用本地/频道,则您有两个始发频道和两个 CDR。
当您参加转接时,您的手机可能会先创建另一个(第三个)通道到目的地,然后与它通话,然后桥接旧通道。
您可以通过在其上发出NoCDR命令来消除某些通道,但它很复杂,并且对于某些有人值守的转接/硬件电话来说不会。
队列呼叫有一个呼入通道,以及与呼叫相关的无限(可以轻松100+)呼出通道。这就是它的工作原理。
在大多数情况下,您可以通过 linkedid/linkeduniq 字段连接所有这些 CDR。