Identificación de conferencias y transferencias mediante los registros de contacto de Amazon Connect - Amazon Connect

Las traducciones son generadas a través de traducción automática. En caso de conflicto entre la traducción y la version original de inglés, prevalecerá la version en inglés.

Identificación de conferencias y transferencias mediante los registros de contacto de Amazon Connect

Los registros de contacto capturan los eventos asociados a un contacto en el centro de contacto. Para cada contacto nuevo, Amazon Connect crea un registro de contacto y le asigna un identificador de contacto único.

Cada vez que un agente consulta a otro agente (interno de Amazon Connect o externo, mediante un número de teléfono gratuito o directo), Amazon Connect crea un registro de contactos del tramo de consulta y emite un nuevo identificador de contacto para este tramo.

El registro de contacto principal y cualquier registro de contacto posterior a la etapa de consulta se pueden vincular mediante varios campos de identificación de contacto, por ejemplo, la ID de contacto inicial, la ID de contacto siguiente y la ID de contacto anterior.

Este tema explica cómo puede utilizar estos campos para diferenciar entre conferencias y transferencias en los registros de contactos. También proporciona una lógica para establecer el tipo de operación informativa: llamada de asesoría, de conferencia o de transferencia.

Terminología

En estos temas se utiliza la siguiente terminología:

Llamada informativa

Una llamada en la que participan tres participantes:

  1. El iniciador, por ejemplo, un cliente

  2. El destinatario, por ejemplo, un agente

  3. Un participante consultado, por ejemplo, un supervisor o un traductor externo

Una llamada informativa puede terminar siendo una llamada de asesoría, de transferencia o de conferencia.

Llamada de asesoría

Llamada en la que el agente destinatario consulta a otro participante (por ejemplo, un agente de la misma instancia de Amazon Connect o una entidad externa), mientras el iniciador queda en espera.

Tras desconectar una llamada, Amazon Connect coloca al agente en un estado de trabajo después de llamada (ACW). El registro del contacto se actualiza con la marca de tiempo en la que se introdujo este estado. En el caso de las llamadas de asesoría, el participante consultado se desconecta antes que el cliente.

El registro de contactos registra la fecha y hora en la que el agente fue colocado en estado ACW bajo AfterContactWorkStartTimestamp.

Llamada de transferencia

El destinatario transfiere el iniciador al participante consultado. En este caso, el agente destinatario entra en ACW antes que el agente consultado.

Llamada de conferencia

El destinatario conecta al iniciador con el participante consultado (llamada con tres participantes).

Amazon Connect permite que haya más de tres participantes. En el caso de las llamadas internas, el participante consultado entra en ACW antes que el destinatario, tanto en las situaciones de asesoría como de conferencia. Sin embargo, la diferencia es que en una conferencia, el participante consultado también puede hablar con el cliente, mientras que en una asesoría el destinatario deja al cliente en espera.

En las secciones siguientes, se explica cómo identificar cada uno de estos tipos de llamadas en un registro de contacto.

Registros de contactos para llamadas informativas

Supongamos que un cliente llama al agente 1. El agente no transfiere la llamada ni se asesora con otras personas. Cuando se desconecta la llamada, el registro del contacto se parece al siguiente ejemplo (solo se muestran los campos relevantes):

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

Si el agente 1 iniciara una llamada informativa con otro agente (agente 2), sería una asesoría, una transferencia o una conferencia.

El siguiente ejemplo de registro de contactos muestra cómo afectaría esto al agente iniciador (agente 1) y al agente destinatario (agente 2):

  • Agente iniciador (agente 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", . . }
  • Agente destinatario (agente 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", . . }

    La relación entre las dos partes del registro del contacto se muestra en el siguiente diagrama:

    La relación entre el agente 1 y el agente 2 durante una llamada informativa

    El agente 1 (A1) y el agente 2 (A2) están unidos por lo siguiente:

    • N = ID de contacto siguiente. Este campo aparece en el registro de contacto del tramo inicial. Este es el identificador de contacto del último agente con el que este agente consultó (en este caso, el último agente es A2).

    • P = ID de contacto anterior. Este campo aparece en el registro del contacto del tramo de consulta. Es el ID de contacto del tramo que llamó a este tramo. En este caso, A1.

    Cosas que no aparecen en el diagrama:

    • ID de contacto inicial: es el ID de contacto de la primera interacción entre el agente 1 (A1) y el cliente (C).

    • ID de contacto: es el identificador único de una interacción determinada.

    El ID de contacto, el ID de contacto inicial y el ID de contacto anterior son atributos del sistema. Para obtener una descripción de cada elemento, consulte Atributos del sistema.

Este modelo se puede extender a una llamada de consulta en la que participen varios agentes. A continuación, se muestran ejemplos de casos de uso donde se aprecian estas posibilidades de extensión.

  • Caso de uso 1: el agente 1 invita al agente 2, el agente 2 invita al agente 3 y el agente 3 invita al agente 4. El ID de contacto anterior es siempre el agente anterior. El siguiente diagrama ilustra este caso de uso.

    A1 invita a A2, A2 invita a A3, A3 invita a A4; el ID de contacto anterior es siempre el agente anterior.
  • Caso de uso 2: el agente 1 invita al agente 2, el agente 1 invita al agente 3 y el agente 1 invita al agente 4. El ID de contacto anterior es siempre el agente 1. El siguiente diagrama ilustra este caso de uso.

    A1 invita a A2, A1 invita a A3 y A1 invita a A4; el ID de contacto anterior es siempre A1.
  • Caso de uso 3: el agente 1 invita al agente 2, el agente 2 invita al agente 4 y al agente 5 y el agente 1 invita al agente 3. El ID de contacto anterior de los agentes 2 y 3 es el agente 1. El ID de contacto anterior de los agentes 4 y 5 es el agente 2. El siguiente diagrama ilustra este caso de uso.

    A1 invita a A2, A2 invita a A4 y A5, A1 invita a A3.

Cómo identificar las llamadas informativas

  1. Paso 1: agrupación de todos los tramos asociados al contacto principal

  2. Paso 2: identificación de la relación entre cada par utilizando sus campos de ID de contacto (ID de contacto anterior, ID de contacto siguiente, ID de contacto inicial e ID de contacto). Examine los campos adicionales del registro de contactos para identificar el tipo de operación informativa: asesoría, transferencia o conferencia.

Paso 1: agrupación de todos los tramos asociados al contacto principal

Este paso le ayuda a agrupar todas las llamadas iniciadas por un iniciador o interlocutor determinado. Los campos de interés son ID de contacto, ID de contacto anterior, ID de contacto siguiente, ID de contacto inicial e ID de contacto. Esto también le ayuda a entender el número de etapas necesarias para resolver la llamada. El flujo de trabajo es el siguiente:

  1. Establecer el iniciador: este es el registro de contacto donde el campo InitialContactId es NULL. Además, PreviousContactId también es NULL para este registro.

  2. Cada registro de contacto en el que el campo InitialContactId es igual al ContactId del registro de contacto iniciador está relacionado con este registro de contacto.

Paso 2: identificación de la relación entre cada par utilizando sus campos de ID de contacto

Puede utilizar la siguiente lógica para distinguir entre asesorías, transferencias y conferencias. La lógica utiliza campos de marca de tiempo anotados en el registro de contactos. Todos los campos relevantes se han marcado como code.

Llamadas de asesoría

El iniciador consulta con otra parte, dentro de la misma instancia de Amazon Connect (interna) o externa a esa instancia (externa), mediante un DID o un número gratuito.

  • Características de las asesorías internas:

    • El agente consultado entra en ACW antes que el agente iniciador

    • El agente consultado nunca habla con el cliente, ya que el iniciador lo ha puesto en espera. Por lo tanto, el campo AgentInteractionDuration para el agente consultado es ZERO.

  • Características de las asesorías externas:

    • La duración de la espera del cliente del iniciador es superior a la duración de la interacción del participante externo (ExternalThirdPartyInteractionDuration).

Llamadas de conferencia

El iniciador abre una conferencia con otra parte, dentro de la misma instancia de Amazon Connect (interna) o externa a esa instancia (externa), mediante un DID o un número gratuito.

  • Características de las asesorías internas:

    • El agente consultado entra en ACW antes que el agente iniciador

    • El agente consultado habla con el cliente: AgentInteractionDuration no es ZERO.

  • Características de las asesorías externas:

    • La duración de la espera del cliente del iniciador es inferior a la duración de la interacción del participante externo (ExternalThirdPartyInteractionDuration). Esto significa que se puso al cliente en espera por un momento y, a continuación, todos los participantes participaron en la llamada.

Transferir llamadas

El iniciador consulta con otra parte, dentro de la misma instancia de Amazon Connect (interna) o externa a esa instancia (externa), mediante un DID o un número gratuito.

  • Características de las asesorías internas:

    • El agente consultado entra en ACW después del agente iniciador.

    • El campo TransferCompletedTimestamp no es ZERO para el agente iniciador.

  • Características de las asesorías externas:

    • El iniciador entra en ACW (AfterContactWorkStartTimestamp) antes de desconectar el tramo externo (DisconnectTimestamp).

    • El campo TransferCompletedTimestamp no es ZERO para el agente iniciador.

Fragmentos de código

Los siguientes fragmentos de código de ejemplo (en SQL, Java Script y Python) muestran cómo identificar las llamadas de conferencia, transferencia e información mediante el uso de la lógica descrita en la sección anterior. Estos fragmentos se proporcionan a modo de ejemplo y no están destinados a la producción.

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