Identificar conferências e transferências usando os registros de contato do Amazon Connect - Amazon Connect

As traduções são geradas por tradução automática. Em caso de conflito entre o conteúdo da tradução e da versão original em inglês, a versão em inglês prevalecerá.

Identificar conferências e transferências usando os registros de contato do Amazon Connect

Os registros de contato capturam os eventos associados à central de atendimento. Para cada novo contato, o Amazon Connect cria um registro de contato e atribui um ID de contato exclusivo ao contato.

Cada vez que um atendente consulta outro atendente (interno ao Amazon Connect ou externo, usando um número de chamada direta ou gratuita), o Amazon Connect cria um registro de contato de trecho de consulta e emite um novo ID de contato para esse trecho.

O registro de contato principal e qualquer registro de contato de trecho de consulta subsequente podem ser vinculados por vários campos de ID de contato, por exemplo, ID de contato inicial, próximo ID de contato e ID de contato anterior.

Este tópico explica como usar esses campos para diferenciar conferências e transferências em registros de contato. Também fornece uma lógica para estabelecer o tipo de operação consultiva: chamada de consulta, conferência ou transferência.

Terminologia

A seguinte terminologia é usada neste tópico:

Chamada consultiva

Uma chamada envolvendo três participantes:

  1. O iniciador, por exemplo, um cliente.

  2. O destinatário, por exemplo, um atendente.

  3. Um participante consultado, por exemplo, um supervisor ou um tradutor externo terceiro.

Uma chamada consultiva pode acabar sendo uma chamada de consulta, uma chamada de transferência ou uma teleconferência.

Chamada de consulta

Uma chamada na qual o atendente destinatário consulta outro participante (por exemplo, um atendente na mesma instância do Amazon Connect ou uma entidade externa), enquanto o iniciador é colocado em espera.

Depois que uma chamada é desligada, o Amazon Connect coloca o atendente em um estado de Trabalho pós-atendimento (ACW). O registro do contato é atualizado com o carimbo de data/hora em que esse estado foi inserido. No caso de chamadas de consulta, o participante consultado se desconecta antes do cliente.

O registro de contato registra o carimbo de data/hora em que o atendente foi colocado no estado ACW em AfterContactWorkStartTimestamp.

Transferir chamada

O destinatário transfere o iniciador para o participante consultado. Nesse caso, o atendente destinatário entra no ACW antes do atendente consultado.

Chamada em conferência

O destinatário coloca o iniciador em conferência com o participante consultado (chamada a três).

O Amazon Connect permite que mais de três pessoas participem de uma conferência. Para chamadas internas, o participante consultado entra no ACW antes do destinatário nas situações de consulta e conferência. No entanto, a diferença é que em uma situação de conferência, o participante consultado também pode falar com o cliente, enquanto em um caso de consulta, o cliente é colocado em espera pelo destinatário.

As seções a seguir explicam como identificar cada um desses tipos de chamada em um registro de contato.

Registros de contato para chamadas consultivas

Digamos que o cliente ligue para o Atendente 1. O atendente não transfere nem consulta outras pessoas. Quando a chamada é desligada, o registro do contato se parece com o seguinte exemplo (somente os campos relevantes são mostrados):

{ "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 o Atendente 1 iniciasse uma chamada consultiva com outro atendente (Atendente 2), seria uma consulta, uma transferência ou uma conferência.

O seguinte exemplo de registro de contato mostra como isso seria para o atendente iniciador (Atendente 1) e o atendente destinatário (Atendente 2):

  • Atendente iniciador (Atendente 1)

    { "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", . . }
  • Atendente destinatário (Atendente 2)

    { "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", . . }

    O relacionamento entre as duas partes do registro de contato é mostrado no seguinte diagrama:

    A relação entre o Atendente 1 e o Atendente 2 durante uma chamada consultiva.

    Em que o Atendente 1 (A1) e o Atendente 2 (A2) estão vinculados por:

    • N = ID do próximo contato. Esse campo aparece no registro de contato do trecho inicial. Esse é o ID de contato do último atendente consultado por esse atendente (nesse caso, o último atendente é A2).

    • P = ID do contato anterior. Esse campo aparece no registro de contato do trecho de consulta. Esse é o ID de contato do trecho que chamou esse trecho. Nesse caso, é A1.

    Não são mostrados no diagrama:

    • ID de contato inicial: é o ID de contato da primeira interação entre o Atendente 1 (A1) e o cliente (C).

    • ID de contato: é o identificador exclusivo de uma interação específica.

    ID de contato, ID de contato inicial e ID de contato anterior são atributos do sistema. Para ver as descrições de cada um, consulte Atributos do sistema.

Esse modelo pode ser estendido para uma chamada de consulta que envolve vários atendentes. Veja a seguir exemplos de casos de uso de como ele pode ser estendido.

  • Caso de uso 1: o Atendente 1 convida o Atendente 2, o Atendente 2 convida o Atendente 3 e o Atendente 3 convida o Atendente 4. O ID de contato anterior é sempre o atendente anterior. O diagrama a seguir ilustra esse caso de uso.

    O A1 convida o A2, o A2 convida o A3, o A3 convida o A4, o ID do contato anterior é sempre o atendente anterior.
  • Caso de uso 2: o Atendente 1 convida o Atendente 2, o Atendente 1 convida o Atendente 3 e o Atendente 1 convida o Atendente 4. O ID do contato anterior é sempre o Atendente 1. O diagrama a seguir ilustra esse caso de uso.

    O A1 convida o Atendente 2, o A1 convida o A3 e o A1 convida o A4, o ID de contato anterior é sempre A1.
  • Caso de uso 3: o Atendente 1 convida o Atendente 2, o Atendente 2 convida o Atendente 4 e o Atendente 5 convida o Atendente 3. O ID do contato anterior dos Atendentes 2 e 3 é Atendente 1. Para os Atendentes 4 e 5, o ID do contato anterior é o Atendente 2. O diagrama a seguir ilustra esse caso de uso.

    O A1 convida o A2, o A2 convida o A4 e o A5, o A1 convida o A3.

Como identificar chamadas consultivas

  1. Etapa 1: agrupar todos os trechos associados ao contato principal

  2. Etapa 2: identificar a relação entre cada par usando seus campos de ID de contato(ID do contato anterior, ID do próximo contato, ID do contato inicial e ID de contato). Examine os campos adicionais no registro de contato para identificar o tipo de operação consultiva: Consulta/Transferência ou Conferência.

Etapa 1: agrupar todos os trechos associados ao contato principal

Essa etapa ajuda você a agrupar todas as chamadas que foram iniciadas por um iniciador/chamador específico. Os campos de interesse são ID de contato, ID do contato anterior, ID do próximo contato, ID do contato inicial e ID de contato. Isso também ajuda você a entender o número de etapas necessárias para resolver a chamada. O fluxo de trabalho referente a esse requisito é o seguinte:

  1. Estabeleça o iniciador: é o registro de contato em que o campo InitialContactId é NULL. Além disso, PreviousContactId também é NULL para esse registro.

  2. Cada registro de contato em que o campo InitialContactId é igual ao do registro de contato do iniciador ContactId está relacionado a esse registro de contato.

Etapa 2: identificar a relação entre cada par usando seus campos de ID de contato

Você pode usar a lógica a seguir para identificar consultas versus transferências versus conferências. A lógica usa campos de carimbo de data/hora anotados no registro do contato. Todos os campos relevantes foram marcados como code.

Chamadas de consulta

O iniciador consulta outra parte: dentro da mesma instância do Amazon Connect (interna) ou externa a essa instância (externa), usando um DID ou um número gratuito.

  • Características das consultas internas:

    • O atendente consultado entra no ACW antes do atendente iniciador

    • O atendente consultado nunca fala com o cliente, isso ocorre porque o cliente foi colocado em espera pelo iniciador. Portanto, o campo AgentInteractionDuration para o atendente consultado é ZERO.

  • Característica de consulta externa:

    • A duração da espera do cliente do iniciador é maior do que a duração da interação da parte externa (ExternalThirdPartyInteractionDuration).

Teleconferências

Conferências de iniciadores com outro participante na mesma instância do Amazon Connect (interna) ou externa a essa instância (externa), usando um DID ou um número gratuito.

  • Características das consultas internas:

    • O atendente consultado entra no ACW antes do atendente iniciador.

    • O atendente consultado fala com o cliente: AgentInteractionDuration é diferente de ZERO.

  • Características da consulta externa:

    • A duração da espera do cliente do iniciador é menor do que a duração da interação da parte externa (ExternalThirdPartyInteractionDuration). Isso significa que o cliente foi colocado brevemente em espera e, depois, todos participaram da chamada.

Transferir chamadas

O iniciador consulta outra parte: dentro da mesma instância do Amazon Connect (interna) ou externa a essa instância (externa), usando um DID ou um número gratuito.

  • Características das consultas internas:

    • O atendente consultado entra no ACW depois do atendente iniciador.

    • O campo TransferCompletedTimestamp é diferente de ZERO para o atendente iniciador.

  • Características da consulta externa:

    • O iniciador entra no ACW (AfterContactWorkStartTimestamp) antes que o trecho externo seja desconectado (DisconnectTimestamp).

    • O campo TransferCompletedTimestamp é diferente de ZERO para o atendente iniciador.

Trechos de código

Os exemplos de trechos de código a seguir, em SQL, Java script e Python, demonstram como identificar chamadas de conferência, transferência e consultoria utilizando a lógica descrita na seção anterior. Esses trechos são fornecidos como exemplo e não se destinam à produção.

Código 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

Código 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()

Código 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(); }