Le traduzioni sono generate tramite traduzione automatica. In caso di conflitto tra il contenuto di una traduzione e la versione originale in Inglese, quest'ultima prevarrà.
Identificazione di conferenze e trasferimenti utilizzando i record di contatto di Amazon Connect
I record dei contatti acquisiscono gli eventi associati a un contatto nel contact center. Per ogni nuovo contatto, Amazon Connect crea un record di contatto e vi assegna un ID contatto univoco.
Ogni volta che un agente consulta un altro agente (interno ad Amazon Connect o esterno, utilizzando un numero verde o diretto), Amazon Connect crea un record di contatto per il canale di collegamento ed emette un nuovo ID contatto per questo canale.
Il record di contatto principale e qualsiasi record di contatto successivo per il canale di collegamento possono essere collegati tra loro tramite diversi campi ID contatto, ad esempio ID contatto iniziale, ID contatto successivo e ID contatto precedente.
Questo argomento spiega come puoi utilizzare tali campi per differenziare le conferenze dai trasferimenti nei record di contatto. Fornisce inoltre una logica per stabilire il tipo di operazione di collegamento: chiamata di consultazione, conferenza o trasferimento.
Indice
Terminology
In questo argomento viene utilizzata la seguente terminologia:
- Chiamata di collegamento
-
Una chiamata con tre partecipanti:
-
L’iniziatore, ad esempio, un cliente
-
Il destinatario, ad esempio, un agente
-
Un partecipante consultato, ad esempio, un supervisore o un traduttore esterno terzo
Una chiamata di collegamento può finire per essere una chiamata di consultazione, una chiamata di trasferimento o una chiamata in conferenza.
-
- Chiamata di consultazione
-
Una chiamata in cui l’agente destinatario consulta un altro partecipante (ad esempio, un agente nella stessa istanza Amazon Connect o un’entità esterna), mentre l’iniziatore viene messo in attesa.
Dopo che una chiamata viene disconnessa, Amazon Connect colloca l’agente in uno stato ACW (After Call Work). Il record di contatto viene aggiornato con l’orario in cui è stato attivato questo stato. In caso di chiamate di consultazione, il partecipante consultato si disconnette prima del cliente.
Il record di contatto registra l’orario in cui l’agente è stato messo nello stato ACW in
AfterContactWorkStartTimestamp. - Chiamata di trasferimento
-
Il destinatario trasferisce l’iniziatore al partecipante consultato. In questo caso, l’agente destinatario entra nello stato ACW prima dell’agente consultato.
- Conferenza telefonica
-
Il destinatario mette in conferenza l’iniziatore con il partecipante consultato (chiamata a tre vie).
Amazon Connect consente di tenere conferenze con più di tre partecipanti contemporaneamente. Per le chiamate interne, il partecipante consultato entra nello stato ACW prima del destinatario sia in situazioni di consultazione che di conferenza. La differenza, tuttavia, è che in una situazione di conferenza, il partecipante consultato può anche parlare con il cliente, mentre in caso di consultazione, il cliente viene messo in attesa dal destinatario.
Le seguenti sezioni illustrano come identificare ciascuno di questi tipi di chiamate in un record di contatto.
Record di contatto per chiamate di collegamento
Supponiamo che il cliente chiami l’Agent1. L’agente non trasferisce la chiamata ad altre persone né le consulta. Quando la chiamata viene interrotta, il record di contatto è simile al seguente esempio (vengono visualizzati solo i campi pertinenti):
{ "AWSAccountId": "account-id", "Agent": { "ARN": "agent-arn", "AfterContactWorkStartTimestamp": "2024-08-02T17:50:53Z", . . "Username": "Agent1" }, "ContactId": "497f04ca-6de1-408f-9b8a-ec57bcc99b31", . . "InitialContactId": null, "NextContactId": null, "PreviousContactId": null, . . }
Se Agent1 dovesse avviare una chiamata di collegamento con un altro agente (Agent2), si tratterebbe di una consultazione, di un trasferimento o di una conferenza.
Il seguente record di contatto di esempio mostra come verranno visualizzati l’agente iniziante (Agent1) e l’agente destinatario (Agent2):
-
Agente iniziante (Agent1)
{ "Agent": { "ARN": "agent-arn" "AfterContactWorkStartTimestamp": "2024-08-02T17:50:53Z", . . "Username": "Agent1" }, "ContactId": "497f04ca-6de1-408f-9b8a-ec57bcc99b31", "InitialContactId": null, "NextContactId": "6aa058d3-e771-4544-8e93-f5ce9c9003b3", . . } -
Agente destinatario (Agent2)
{ "Agent": { "ARN": "agent-arn", "AfterContactWorkStartTimestamp": "2024-08-02T17:51:07Z", . . "Username": "Agent2" }, "ContactId": "6aa058d3-e771-4544-8e93-f5ce9c9003b3", "InitialContactId": "497f04ca-6de1-408f-9b8a-ec57bcc99b31", "NextContactId": null, "PreviousContactId": "497f04ca-6de1-408f-9b8a-ec57bcc99b31", . . }La relazione tra le due parti del record di contatto è illustrata nel diagramma seguente:
Dove Agent1 (A1) e Agent2 (A2) sono collegati da:
-
N: ID contatto successivo. Questo campo viene visualizzato nel record di contatto per il canale iniziale. Questo è l’ID contatto dell’ultimo agente consultato dall’agente (in questo caso, l’ultimo agente è A2).
-
P = ID contatto precedente. Questo campo viene visualizzato nel record di contatto per il canale di collegamento. Questo è l’ID contatto del canale che ha chiamato questo canale. In questo caso, si tratta di A1.
Non presenti nel diagramma:
-
ID contatto iniziale: è l’ID contatto della prima interazione tra l’Agent1 (A1) e il cliente (C).
-
ID contatto: è l’identificatore univoco di una determinata interazione.
ID contatto, ID contatto iniziale e ID contatto precedente sono attributi di sistema. Per le descrizioni di ciascun contatto, consulta Attributi di sistema.
-
Questo modello può essere esteso a una chiamata di consultazione che coinvolge più agenti. Di seguito sono riportati esempi di casi d’uso su come può essere esteso il modello.
-
Caso d’uso 1: Agent1 invita Agent2, Agent2 invita Agent3 e Agent3 invita Agent4. L’ID contatto precedente è sempre l’agente precedente. Il diagramma seguente illustra questo caso d’uso.
-
Caso d’uso 2: Agent1 invita Agent2, Agent1 invita Agent3 e Agent1 invita Agent4. L’ID contatto precedente è sempre Agent1. Il diagramma seguente illustra questo caso d’uso.
-
Caso d’uso 3: Agent1 invita Agent2, Agent2 invita Agent4 e Agent5, Agent1 invita Agent3. L’ID contatto precedente per gli agenti 2 e 3 è Agent1. Per gli agenti 4 e 5, l’ID di contatto precedente è Agent2. Il diagramma seguente illustra questo caso d’uso.
Come identificare le chiamate di collegamento
-
Fase 1: raggruppare tutti i canali associati al contatto principale
-
Fase 2: identificare la relazione tra ogni coppia utilizzando i relativi campi ID contatto (ID contatto precedente, ID contatto successivo, ID contatto iniziale e ID contatto). Esamina i campi aggiuntivi nel record di contatto per identificare il tipo di operazione di collegamento: Consultazione/Trasferimento o Conferenza.
Fase 1: raggruppare tutti i canali associati al contatto principale
Questa fase consente di raggruppare tutte le chiamate avviate da un determinato iniziatore/chiamante. I campi di interesse sono ID Contatto, ID contatto precedente, ID contatto successivo, ID contatto iniziale e ID contatto. Questo ti aiuta anche a capire il numero di canali necessari per risolvere la chiamata. Il flusso di lavoro per questo è il seguente:
-
Stabilisci l’iniziatore: questo è il record di contatto in cui il campo
InitialContactIdèNULL. Inoltre,PreviousContactIdè ancheNULLper questo record. -
Ogni record di contatto in cui il campo
InitialContactIdè uguale aContactIddel record di contatto dell’iniziatore è correlato a questo record di contatto.
Fase 2: identificare la relazione tra ogni coppia utilizzando i relativi campi ID contatto
Puoi utilizzare la seguente logica per identificare le consultazioni rispetto ai trasferimenti rispetto alle conferenze. La logica utilizza i campi timestamp indicati nel record di contatto. Tutti i campi pertinenti sono stati contrassegnati come code.
Chiamate di consultazione
L’iniziatore consulta un’altra parte, all’interno della stessa istanza Amazon Connect (interna) o esterna a quell’istanza (esterna), utilizzando un DID o un numero verde.
-
Caratteristiche delle consultazioni interne:
-
L’agente consultato entra nello stato ACW prima dell’agente iniziatore
-
L’agente consultato non parla mai con il cliente, perché il cliente è stato messo in attesa dall’iniziatore. Pertanto il campo
AgentInteractionDurationper l’agente consultato è ZERO.
-
-
Caratteristica della consultazione esterna:
-
La durata di attesa del cliente dell’iniziatore è superiore alla durata dell’interazione della parte esterna (
ExternalThirdPartyInteractionDuration).
-
Chiamate in conferenza
L’iniziatore entra in conferenza con un altro partecipante all’interno della stessa istanza Amazon Connect (interna) o esterna a quell’istanza (esterna), utilizzando un DID o un numero verde.
-
Caratteristiche delle consultazioni interne:
-
L’agente consultato entra nello stato ACW prima dell’agente iniziatore.
-
L’agente consultato parla con il cliente:
AgentInteractionDurationè diverso da ZERO.
-
-
Caratteristiche della consultazione esterna:
-
La durata dell’attesa del cliente dell’iniziatore è inferiore alla durata dell’interazione della parte esterna (
ExternalThirdPartyInteractionDuration). Ciò significa che il cliente è stato messo in attesa per un breve periodo di tempo e quindi tutti i partecipanti hanno partecipato alla chiamata.
-
Trasferire le chiamate
L’iniziatore consulta un’altra parte, all’interno della stessa istanza Amazon Connect (interna) o esterna a quell’istanza (esterna), utilizzando un DID o un numero verde.
-
Caratteristiche delle consultazioni interne:
-
L’agente consultato entra nello stato ACW dopo l’agente iniziatore.
-
Il campo
TransferCompletedTimestampè diverso da ZERO per l’agente iniziatore.
-
-
Caratteristiche della consultazione esterna:
-
L’iniziatore entra nello stato ACW (
AfterContactWorkStartTimestamp) prima che il canale esterno venga disconnesso (DisconnectTimestamp). -
Il campo
TransferCompletedTimestampè diverso da ZERO per l’agente iniziatore.
-
Frammenti di codice
I seguenti frammenti di codice di esempio, in SQL, Java script e Python, dimostrano come identificare chiamate di conferenza, trasferimento e di collegamento sfruttando la logica descritta nella sezione precedente. Questi frammenti sono forniti a titolo di esempio e non sono destinati alla produzione.
Codice SQL
-- Conference transfer query DO NOT EDIT -- SELECT current_cr.contact_id, current_cr.initial_contact_id, current_cr.previous_contact_id, current_cr.next_contact_id, previous_cr.agent_username as initiator_agent_username, COALESCE ( current_cr.agent_username, current_cr.customer_endpoint_address ) as recipient_agent_username, current_cr.agent_connected_to_agent_timestamp, current_cr.agent_after_contact_work_start_timestamp, current_cr.transfer_completed_timestamp, CASE WHEN previous_cr.agent_after_contact_work_start_timestamp < current_cr.agent_after_contact_work_start_timestamp AND previous_cr.transfer_completed_timestamp IS NOT NULL THEN 'TRANSFER' WHEN previous_cr.agent_after_contact_work_start_timestamp > current_cr.agent_after_contact_work_start_timestamp AND current_cr.agent_interaction_duration_ms <= 2000 THEN 'CONSULT' WHEN previous_cr.agent_after_contact_work_start_timestamp > current_cr.agent_after_contact_work_start_timestamp AND current_cr.agent_interaction_duration_ms > 2000 THEN 'CONFERENCE' WHEN current_cr.agent_username is NULL AND current_cr.initiation_method = 'EXTERNAL_OUTBOUND' AND previous_cr.agent_after_contact_work_start_timestamp > current_cr.disconnect_timestamp AND previous_cr.agent_customer_hold_duration_ms > current_cr.external_third_party_interaction_duration_ms THEN 'EXTERNAL_CONSULT' WHEN current_cr.agent_username is NULL AND current_cr.initiation_method = 'EXTERNAL_OUTBOUND' AND previous_cr.agent_after_contact_work_start_timestamp > current_cr.disconnect_timestamp AND previous_cr.agent_customer_hold_duration_ms < current_cr.external_third_party_interaction_duration_ms THEN 'EXTERNAL_CONFERENCE' WHEN current_cr.agent_username is NULL AND current_cr.initiation_method = 'EXTERNAL_OUTBOUND' AND current_cr.disconnect_timestamp > previous_cr.transfer_completed_timestamp THEN 'EXTERNAL_TRANSFER' ELSE 'START' END AS TYPE FROM contact_record_link current_cr LEFT JOIN contact_record_link previous_cr ON previous_cr.contact_id = current_cr.previous_contact_id WHERE ( -- INPUT CONTACT ID -- current_cr.initial_contact_id = 'A CONTACT ID' or current_cr.contact_id = 'SAME CONTACT ID AS ABOVE' ) order by current_cr.agent_connected_to_agent_timestamp asc
Codice Python
"""Module Compare CTR's and establish relation""" ############################################################################### # Usage python ctr_processor.py [Initial Contact ID] # Example: python CTR_Processor.py 497f04ca-6de1-408f-9b8a-ec57bcc99b31 # # Have your CTR record JSON files in the same directory as this Python module # and execute the module as noted above. The input parameter is the # Initial Contact ID / the Contact ID of the first leg of the call. # ####################################################################z########### import json import re import os import sys from dateutil import parser PATH_OF_FILES = './' JSON = '.json' ENCODING = 'UTF-8' INTERACTION_DURN_THRESHOLD = 2 TYPE_INITIAL = 'STAND ALONE' TYPE_CONSULT = 'CONSULT' TYPE_EXT_CONSULT = 'EXT_CONSULT' TYPE_EXT_CONF = 'EXT_CONFERENCE' TYPE_CONFERENCE = 'CONFERENCE' TYPE_TRANSFER = 'TRANSFER' TYPE_UNKNOWN = 'UNKNOWN' CONTACT_STATE_INT = 'INTERMEDIATE' CONTACT_STATE_FINAL = 'FINAL' CONTACT_STATE_START = 'START' PRINT_INDENT = 4 def process_ctr_records(ctr_array): """ Function to process CTR Records""" relation = {} output_list = [] if ctr_array is None : return None for i, a_record in enumerate(ctr_array): if (prev_cid := a_record.get('PreviousContactId', None)) is not None: if (parent_ctr := get_parent_node(ctr_array, a_record['ContactId'], prev_cid)) is not None: relation = establish_relation(parent_ctr, a_record) else: relation = establish_parent(a_record) if relation is not None: output_list.append(relation) return output_list def establish_parent(a_ctr): """ Establish the first record - the one that doesn't have a Previous Contact ID""" if a_ctr.get('Agent', None) is not None: return { 'Agent': a_ctr['Agent']['Username'] ,'ConnectedToAgentTimestamp': a_ctr['Agent']['ConnectedToAgentTimestamp'] ,'Root Contact ID': a_ctr['ContactId'] ,'Type': TYPE_INITIAL ,'Contact State': CONTACT_STATE_START } def establish_relation(parent, child): """ Establish Conf / Transfer / Consult relation between two Agents""" if is_external_call(child): return establish_external_relation(parent, child) else: return establish_internal_relation(parent, child) def establish_external_relation(parent, child): """ Establish Conf / Transfer / Consult relation between two Agents - External call""" ret = { 'Parties': parent['Agent']['Username'] + ' <-> External:' + child['CustomerEndpoint']['Address'] ,'Contact State': parent.get('Contact State', CONTACT_STATE_INT) ,'ConnectedToAgentTimestamp': child['ConnectedToSystemTimestamp'] } parent_acw_start_ts = parser.parse(parent['Agent']['AfterContactWorkStartTimestamp']) child_disconnect_ts = parser.parse(child['DisconnectTimestamp']) if (parent_acw_start_ts - child_disconnect_ts).total_seconds() > 0: # Parent ended after child: Consult or conference ret['Type'] = TYPE_EXT_CONSULT if (parent['Agent']['CustomerHoldDuration'] - child['ExternalThirdParty']['ExternalThirdPartyInteractionDuration']) > INTERACTION_DURN_THRESHOLD else TYPE_EXT_CONF elif ((transfer_completed_ts := parser.parse(parent.get('TransferCompletedTimestamp', None))) is not None) and \ ((child_disconnect_ts - transfer_completed_ts).total_seconds() > 0): # ACW started after transfer was completed ret['Type'] = TYPE_TRANSFER return ret def establish_internal_relation(parent, child): """ Establish Conf / Transfer / Consult relation between two Agents - Internal call""" ret = { 'Parties': parent['Agent']['Username'] + ' <-> ' + child['Agent']['Username'] ,'Contact State': parent.get('Contact State', CONTACT_STATE_INT) ,'Child Contact ID': child.get('ContactId', 'NOTHING') ,'ConnectedToAgentTimestamp': child['Agent']['ConnectedToAgentTimestamp'] } parent_acw_start_ts = parser.parse(parent['Agent']['AfterContactWorkStartTimestamp']) child_acw_start_ts = parser.parse(child['Agent']['AfterContactWorkStartTimestamp']) if (parent_acw_start_ts - child_acw_start_ts).total_seconds() > 0: # Parent ended after child: Consult or conference ret['Type'] = TYPE_CONSULT if child['Agent']['AgentInteractionDuration'] < INTERACTION_DURN_THRESHOLD else TYPE_CONFERENCE elif ((transfer_completed_ts := parser.parse(parent.get('TransferCompletedTimestamp', None))) is not None) and \ ((child_acw_start_ts - transfer_completed_ts).total_seconds() > 0): # ACW started after transfer was completed ret['Type'] = TYPE_TRANSFER return ret def is_external_call(a_record): """Is this an external call """ if (a_record.get('Agent', None) is None and a_record.get('InitiationMethod', None) == 'EXTERNAL_OUTBOUND'): return True return False def get_parent_node(ctr_array, child_cid, child_prev_cid): """ Get the parent node when we have a Previous Contact ID""" for i, a_record in enumerate(ctr_array): if (parent_cid := a_record.get('ContactId', None)) is not None: if compare_strings(parent_cid, child_prev_cid): if (parent_next_cid := a_record.get('NextContactId', None)) is not None: if compare_strings(parent_next_cid, child_cid): return a_record | {'Contact State': CONTACT_STATE_FINAL} else: return a_record else: return a_record | {'Contact State': CONTACT_STATE_INT} def compare_strings(s1, s2): """ Compare two Contact IDs""" if s1 is None or s2 is None : return False return re.search(re.compile(s2), s1) def read_all_ctr_records(a_cid): """ Read all the CTR records for a given Initial Contact ID. Modify for S3 read""" ctr_array = [] for file_name in [file for file in os.listdir(PATH_OF_FILES) if file.endswith(JSON)]: with open(PATH_OF_FILES + file_name, encoding=ENCODING) as json_file: try: a_ctr = json.load(json_file) except ValueError: print('Error in parsing JSON. File name:[', file_name, ']') if a_ctr is not None: c_id = a_ctr['ContactId'] init_cid = a_ctr.get('InitialContactId', None) if compare_strings(a_cid, c_id): ctr_array.append(a_ctr) elif compare_strings(a_cid, init_cid): ctr_array.append(a_ctr) return ctr_array def main(): """ Entry point""" if len(sys.argv) < 2: print('Incorrect number of arguments (', len(sys.argv), ') --> python ctr_processor.py [Initial Contact ID]') return else: output_list = process_ctr_records(read_all_ctr_records(sys.argv[1])) if output_list is not None and len(output_list) > 0: output_list.sort(key=lambda x: x['ConnectedToAgentTimestamp']) for i, an_entry in enumerate(output_list): print(json.dumps(an_entry, indent=PRINT_INDENT)) else: print('Unable to find Contact ID:[', sys.argv[1], '] in the input CTR Records. Please check the files and try again.') if __name__ == "__main__": main()
Codice JS
// Has a dependency on the following Node.js modules: - date-fns, fs, path //sample input: node index.js 497f04ca-6de1-408f-9b8a-ec57bcc99b31 const fs = require('fs'); const path = require('path'); const { parseISO } = require('date-fns'); const PATH_OF_FILES = './'; const JSON_EXT = '.json'; const ENCODING = 'UTF-8'; const INTERACTION_DURATION_THRESHOLD = 2; const CONTACT_TYPES = { INITIAL: 'STAND ALONE', CONSULT: 'CONSULT', EXTERNAL_CONSULT: 'EXT_CONSULT', EXTERNAL_CONFERENCE: 'EXT_CONFERENCE', CONFERENCE: 'CONFERENCE', TRANSFER: 'TRANSFER', EXTERNAL_TRANSFER: 'EXT_TRANSFER', }; const CONTACT_STATES = { INTERMEDIATE: 'INTERMEDIATE', FINAL: 'FINAL', START: 'START', }; const PRINT_INDENT = 4; function processCtrRecords(ctrArray) { if (!ctrArray) return null; const outputList = []; ctrArray.forEach(record => { let relation = null; const prevCid = record.PreviousContactId; if (prevCid) { const parentRecord = findParentRecord(ctrArray, record.ContactId, prevCid); if (parentRecord) { relation = establishRelation(parentRecord, record); } } else { relation = establishInitialRecord(record); } if (relation) { outputList.push(relation); } }); return outputList; } function establishInitialRecord(record) { if (record.Agent) { return { 'Agent': record.Agent.Username, 'ConnectedToAgentTimestamp': record.Agent.ConnectedToAgentTimestamp, 'Root Contact ID': record.ContactId, 'Type': CONTACT_TYPES.INITIAL, 'Contact State': CONTACT_STATES.START, }; } } function establishRelation(parent, child) { return isExternalCall(child) ? establishExternalRelation(parent, child) : establishInternalRelation(parent, child); } function establishExternalRelation(parent, child) { const parentAcwStartTs = parent.Agent?.AfterContactWorkStartTimestamp ? parseISO(parent.Agent.AfterContactWorkStartTimestamp) : null; const childDisconnectTs = child.DisconnectTimestamp ? parseISO(child.DisconnectTimestamp) : null; const relation = { 'Parties': `${parent.Agent.Username} <-> External:${child.CustomerEndpoint.Address}`, 'Contact State': parent['Contact State'] || CONTACT_STATES.INTERMEDIATE, 'ConnectedToAgentTimestamp': child.ConnectedToSystemTimestamp, }; if (parentAcwStartTs && childDisconnectTs && (parentAcwStartTs - childDisconnectTs) > 0) { if (parent.Agent.CustomerHoldDuration - child.ExternalThirdParty.ExternalThirdPartyInteractionDuration > INTERACTION_DURATION_THRESHOLD) { relation['Type'] = CONTACT_TYPES.EXTERNAL_CONSULT; } else { relation['Type'] = CONTACT_TYPES.EXTERNAL_CONFERENCE; } } else if (parent.TransferCompletedTimestamp) { const transferCompletedTs = parseISO(parent.TransferCompletedTimestamp); if (transferCompletedTs && childDisconnectTs && (childDisconnectTs - transferCompletedTs) > 0) { relation['Type'] = CONTACT_TYPES.EXTERNAL_TRANSFER; } } return relation; } function establishInternalRelation(parent, child) { const parentAcwStartTs = parent.Agent?.AfterContactWorkStartTimestamp ? parseISO(parent.Agent.AfterContactWorkStartTimestamp) : null; const childAcwStartTs = child.Agent?.AfterContactWorkStartTimestamp ? parseISO(child.Agent.AfterContactWorkStartTimestamp) : null; const relation = { 'Parties': `${parent.Agent.Username} <-> ${child.Agent.Username}`, 'Contact State': parent['Contact State'] || CONTACT_STATES.INTERMEDIATE, 'Child Contact ID': child.ContactId || 'NOTHING', 'ConnectedToAgentTimestamp': child.Agent.ConnectedToAgentTimestamp, }; if (parentAcwStartTs && childAcwStartTs && (parentAcwStartTs - childAcwStartTs) > 0) { relation['Type'] = child.Agent.AgentInteractionDuration < INTERACTION_DURATION_THRESHOLD ? CONTACT_TYPES.CONSULT : CONTACT_TYPES.CONFERENCE; } else if (parent.TransferCompletedTimestamp) { const transferCompletedTs = parseISO(parent.TransferCompletedTimestamp); if (transferCompletedTs && childAcwStartTs && (childAcwStartTs - transferCompletedTs) > 0) { relation['Type'] = CONTACT_TYPES.TRANSFER; } } return relation; } function isExternalCall(record) { return !record.Agent && record.InitiationMethod === 'EXTERNAL_OUTBOUND'; } function findParentRecord(ctrArray, childCid, childPrevCid) { for (const record of ctrArray) { const parentCid = record.ContactId; if (compareStrings(parentCid, childPrevCid)) { const parentNextCid = record.NextContactId; if (parentNextCid && compareStrings(parentNextCid, childCid)) { return { ...record, 'Contact State': CONTACT_STATES.FINAL }; } else { return { ...record, 'Contact State': CONTACT_STATES.INTERMEDIATE }; } } } return null; } function compareStrings(s1, s2) { return s1 && s2 && s1.includes(s2); } function readAllCtrRecords(contactId) { return fs.readdirSync(PATH_OF_FILES) .filter(file => file.endsWith(JSON_EXT)) .map(fileName => JSON.parse(fs.readFileSync(path.join(PATH_OF_FILES, fileName), ENCODING))) .filter(record => compareStrings(contactId, record.ContactId) || compareStrings(contactId, record.InitialContactId)); } function main() { const [initialContactId] = process.argv.slice(2); if (!initialContactId) { console.log('Usage: node index.js [Initial Contact ID]'); return; } const outputList = processCtrRecords(readAllCtrRecords(initialContactId)); if (outputList.length) { outputList.sort((a, b) => new Date(a.ConnectedToAgentTimestamp) - new Date(b.ConnectedToAgentTimestamp)); outputList.forEach(entry => console.log(JSON.stringify(entry, null, PRINT_INDENT))); } else { console.log(`Unable to find Contact ID: [${initialContactId}]. Please check and try again.`); } } if (require.main === module) { main(); }