使用 Amazon Connect 聯絡記錄識別會議和轉接 - Amazon Connect

本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。

使用 Amazon Connect 聯絡記錄識別會議和轉接

聯絡人記錄會擷取與您聯絡中心中的聯絡人有關聯的事件。對於每個新聯絡人,Amazon Connect 會建立聯絡人記錄,並將唯一的聯絡人 ID 指派給聯絡人。

每次客服人員諮詢其他客服人員 (Amazon Connect 內部或外部,使用免付費電話或直接撥入號碼) 時,Amazon Connect 都會建立諮詢腿部聯絡記錄,並為此腿部發出新的聯絡人 ID。

主要聯絡人記錄和任何後續諮詢分支聯絡人記錄可以由數個聯絡人 ID 欄位連結在一起,例如初始聯絡人 ID、下一個聯絡人 ID 和上一個聯絡人 ID。

本主題說明如何使用這些欄位來區分聯絡記錄中的會議和轉接。它還提供建立諮詢操作類型的邏輯:諮詢通話、會議或轉接。

術語

本主題使用下列術語:

諮詢通話

涉及三個參與者的呼叫:

  1. 啟動者,例如客戶

  2. 收件人,例如,客服人員

  3. 已諮詢的參與者,例如主管或外部第三方譯者

諮詢通話最終可能是諮詢通話、轉接通話或會議通話。

諮詢 呼叫

當啟動器處於保留狀態時,收件人客服人員諮詢其他參與者 (例如,相同 Amazon Connect 執行個體或外部實體中的客服人員) 的通話。

通話中斷後,Amazon Connect 會將客服人員置於通話後工作 (ACW) 狀態。聯絡人記錄會更新為進入此狀態時的時間戳記。如果是諮詢通話,諮詢的參與者會比客戶更早中斷連線。

聯絡人記錄會記錄客服人員在 下處於 ACW 狀態時的時間戳記AfterContactWorkStartTimestamp

轉接通話

收件人將啟動者轉移到已諮詢的參與者。在此情況下,收件人客服人員會比諮詢的客服人員更早輸入 ACW。

電話會議

收件人將啟動者與諮詢的參與者進行會議 (三向通話)。

Amazon Connect 允許三個以上的參與者一起參加會議。對於內部通話,在諮詢和會議情況下,諮詢的參與者會比收件人更早進入 ACW。不過,差別在於,在會議情況下,諮詢的參與者也可以與客戶交談,而在諮詢案例中,收件人會保留客戶通話。

下列各節說明如何識別聯絡記錄中每種類型的通話。

諮詢通話的聯絡記錄

假設客戶呼叫 Agent1。客服人員不會轉接或諮詢其他人。通話中斷時,聯絡人記錄看起來像下列範例 (僅顯示相關欄位):

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

如果 Agent1 要起始與其他客服人員 (Agent2) 的諮詢通話,則為諮詢、轉接或會議。

下列聯絡記錄範例顯示這會如何尋找起始客服人員 (Agent1) 和收件人客服人員 (Agent2):

  • 啟動代理程式 (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", . . }
  • 收件人代理程式 (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", . . }

    下圖顯示聯絡記錄的兩個部分之間的關係:

    在諮詢通話期間,客服人員 1 與客服人員 2 之間的關係。

    其中 Agent1 (A1) 和 Agent2 (A2) 連結者:

    • N = 下一個聯絡人 ID。此欄位會出現在初始腿部的聯絡記錄中。這是此客服人員諮詢的最後一個客服人員的聯絡人 ID (在此情況下,最後一個客服人員是 A2)。

    • P = 上一個聯絡人 ID。此欄位會出現在諮詢腿的聯絡記錄中。這是呼叫此腿之腿的聯絡人 ID。在這種情況下,即 A1。

    圖表中未顯示如下:

    • 初始聯絡人 ID:這是Agent1 (A1) 與客戶 (C) 之間第一次互動的聯絡人 ID。

    • 聯絡人 ID:這是指定互動的唯一識別符。

    聯絡人 ID、初始聯絡人 ID 和先前的聯絡人 ID 是系統屬性。如需每個項目的說明,請參閱 系統屬性

此模型可以延伸到涉及多個客服人員的諮詢通話。以下是如何擴展的範例使用案例。

  • 使用案例 1:Agent1 邀請 Agent2, Agent2 邀請 Agent3,Agent3 邀請 Agent4。上一個聯絡人 ID 一律是上一個客服人員。以下圖表說明此使用案例。

    A1 邀請 A2, A2 邀請 A3, A3 邀請 A4,先前的聯絡人 ID 一律是先前的客服人員。
  • 使用案例 2:Agent1 邀請 Agent2, Agent1 邀請 Agent3,而 Agent1 邀請 Agent4。上一個聯絡人 ID 一律為 Agent1。以下圖表說明此使用案例。

    A1 邀請 Agent2, A1 邀請 A3A1 邀請 A4,先前的聯絡人 ID 一律為 A1。
  • 使用案例 3:Agent1 邀請 Agent2, Agent2 邀請 Agent4 和 Agent5, Agent1 邀請 Agent3。Agents2 和 3 的先前聯絡 ID 是 Agent1。對於 Agents4 和 5,先前的聯絡人 ID 是 Agent2。以下圖表說明此使用案例。

    A1 邀請 A2, A2 邀請 A4 和 A5, A1 邀請 A3。

如何識別諮詢通話

  1. 步驟 1:將所有與主要聯絡人相關聯的腿分組

  2. 步驟 2:使用聯絡人 ID 欄位識別每對之間的關係 (上一個聯絡人 ID、下一個聯絡人 ID、初始聯絡人 ID 和聯絡人 ID)。檢查聯絡人記錄中的其他欄位,以識別諮詢操作的類型:諮詢/轉接或會議。

步驟 1:將所有與主要聯絡人相關聯的腿分組

此步驟可協助您將所有由指定啟動者/呼叫者啟動的呼叫分組。感興趣的欄位包括聯絡人 ID、先前的聯絡人 ID、下一個聯絡人 ID、初始聯絡人 ID 和聯絡人 ID。這也有助於您了解解決通話所需的腿數。工作流程如下所示:

  1. 建立啟動者:這是 InitialContactId 欄位為 的聯絡記錄NULL。此外, PreviousContactIdNULL適用於此記錄。

  2. 每個聯絡人記錄,其中 InitialContactId 欄位等於啟動者聯絡人記錄ContactId的 與此聯絡人記錄相關。

步驟 2:使用聯絡人 ID 欄位識別每對之間的關係

您可以使用下列邏輯來識別諮詢與轉接與會議。邏輯使用聯絡人記錄中記下的時間戳記欄位。所有相關欄位都標示為 code

諮詢 呼叫

啟動者會使用 DID 或免付費電話號碼,向同一 Amazon Connect 執行個體 (內部) 或該執行個體 (外部) 外部的另一方諮詢。

  • 內部諮詢特性:

    • 諮詢的代理程式會在啟動器代理程式之前進入 ACW

    • 諮詢的客服人員永遠不會與客戶交談,因為啟動者已將客戶保留通話。因此,所諮詢客服人員AgentInteractionDuration的欄位為 ZERO。

  • 外部諮詢特性:

    • 啟動者的客戶保留期間高於外部方的互動期間 (ExternalThirdPartyInteractionDuration)。

會議呼叫

使用 DID 或免付費電話號碼,與相同 Amazon Connect 執行個體 (內部) 或該執行個體 (外部) 外部的其他參與者進行啟動者會議。

  • 內部諮詢特性:

    • 諮詢的代理程式會在啟動器代理程式之前進入 ACW。

    • 諮詢的客服人員與客戶交談: AgentInteractionDuration 不是 ZERO。

  • 外部諮詢特性:

    • 啟動者的客戶保留持續時間小於外部方的互動持續時間 (ExternalThirdPartyInteractionDuration)。這表示客戶短暫保留通話,然後所有參與者都參與通話。

轉接通話

啟動者會使用 DID 或免付費電話號碼,向同一 Amazon Connect 執行個體 (內部) 或該執行個體 (外部) 外部的另一方諮詢。

  • 內部諮詢特性:

    • 諮詢的代理程式會在啟動器代理程式之後進入 ACW。

    • 啟動器代理程式的欄位TransferCompletedTimestamp不是 ZERO。

  • 外部諮詢特性:

    • 啟動器會先進入 ACW (AfterContactWorkStartTimestamp),再中斷外部腳架的連接 (DisconnectTimestamp)。

    • 啟動器代理程式的欄位TransferCompletedTimestamp不是 ZERO。

程式碼片段

下列在 SQL、Java 指令碼和 Python 中的範例程式碼片段示範如何利用上一節所述的邏輯來識別會議、轉接和諮詢通話。這些程式碼片段為範例,不適用於生產。

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

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

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