npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@servisbot/conversation-runtime

v3.6.0

Published

Runtime SDK for Interacting with ServisBOT conversations

Downloads

51

Readme

conversation-runtime

Runtime SDK for Interacting with ServisBOT conversations

Install

npm install @servisbot/conversation-runtime

Using the Module

Quick Start

Creating a Conversation & Sending/Receiving Messages

if you are looking for synchronous mode see below

import { ConversationConnector, ConversationChannelTypes, IngressEventFactory } from '@servisbot/conversation-runtime';

const organization = 'acme';

const apiKey = '<servisbot-conversation-api-key>'

const endpointAddress = 'acme-MyBot';

// can be "eu-1", "us1" or "usccif1"
const sbRegion = 'us1';

const logger = console;

const conversationConnector = await ConversationConnector.createConnector({
  channelType: ConversationChannelTypes.OnDemandChannelRestWs,
  apiKey,
  endpointAddress,
  logger,
  sbRegion,
  organization,
});

// setup conversation event listeners before creating the conversation
conversationConnector.on('message', (msg) => console.log('MESSAGE', msg));
conversationConnector.on('error', (err) => console.log('ERROR', err));

await conversationConnector.createConversation({
  engagementAdapter: 'L2', engagementAdapterVersion: '1.0.0'
});

const message = IngressEventFactory.createMessage({ message: 'hello world' })

await conversationConnector.send(message);

Note

  • To close the web socket connection add the following to the end of the examples
await conversationConnector.close();

Importing the Module

const { ConversationConnector } = require('@servisbot/conversation-runtime'); //  es5
import { ConversationConnector } from '@servisbot/conversation-runtime'; //  es6

Create an Anonymous Conversation Connector

import { ConversationConnector, ConversationChannelTypes } from '@servisbot/conversation-runtime';

const apiKey = 'conversation-api-key';
const endpointAddress = 'acme-MyBot';
const context = { lang: 'en' };
const sbRegion = 'eu-1';
const organization = 'acme';
const customerReference = 'customer-123';
const regionalEndpoints = [
  { region: 'venus-eu-west-1.eu-1.servisbot.com' },
  { region: 'venus-eu-east-1.eu-1.servisbot.com' },
]

const conversationConnector = await ConversationConnector.createConnector({
  channelType: ConversationChannelTypes.OnDemandChannelRestWs,
  apiKey,
  endpointAddress,
  context,
  logger: console,
  sbRegion,
  organization,
  regionalEndpoints, // optional
  customerReference // optional
});

Regional Endpoints

  • If you do not supply a list of regional endpoints for conversation-runtime to use the runtime will attempt to use default endpoints based on the ServisBOT region that was passed to the runtime when creating the conversation connector instance.
  • The regional endpoints default to the following:
  • eu-private-3
[
  { region: 'venus-eu-west-1.eu-private-3.servisbot.com' }
]
  • eu-private-1
[
  { region: 'venus-eu-west-1.eu-private-1.servisbot.com' }
]
  • eu-1
[
  { region: 'venus-eu-west-1.eu-1.servisbot.com' },
  { region: 'venus-eu-central-1.eu-1.servisbot.com' }
]
  • us1 OR us-1 (some clients pass us-1 instead of us1)
[
  { region: 'venus-us-east-1.us1.servisbot.com' },
  { region: 'venus-us-east-2.us1.servisbot.com' }
]
  • usscif1
[
  { region: 'venus-us-west-1.usscif1.servisbot.com' }
]
  • An error will be thrown if an invalid ServisBOT region is supplied and there is no regional endpoints supplied.

Attach Conversation Handlers

  • You can attach conversation event handlers by using the on function on the created connector.
connector.on('message', (data) => console.log(data));
connector.on('error', (err) => console.log(err));

Conversation Events

Message

  • For events of type TimelineMessage the connector will emit a message event the full message object passed to the event listener.

Host Notifications

There are two types of notifications which can be emitted from the runtime:

  • Standard HostNotification events.
  • Private Host Notification events.

Standard Host Notification Events

  • You can attach a listener for standard host notification events by using the on function on the ConversationConnector instance.
connector.on('HostNotification', (hostNotificationEvent) => console.log(hostNotificationEvent));

Private Host Notification Events

  • Private Host Notification's contents.notification attribute are prefixed with SB:::.

  • This is the list of private host notifications which can be emitted from the runtime:

SB:::UserInputDisabled
SB:::UserInputEnabled
SB:::ValidationPassed
SB:::ValidationFailed
SB:::ProcessingPassed
SB:::ProcessingFailed
SB:::FileProcessorPipelinePassed
SB:::FileProcessorPipelineFailed
SB:::ResetConversation
SB:::Typing
SB:::MessengerOpen
SB:::MessengerClose
SB:::MessengerPopup
SB:::MessengerNavigate
SB:::MessengerStyle
SB:::UserInputNumericEnabled
SB:::UserInputNumericDisabled
  • You can attach a listener for private host notification events by using the on function on the ConversationConnector instance, and specifying one of the private host notification event names listed above.
connector.on('<PRIVATE_HOST_NOTIFICATION_NAME_GOES_HERE>', () => console.log('Received private host notification'));
  • Depending on the private host notification that is emitted, the notification may or may not have data passed along in the event callback.
  • Below is a list of private host notifications which supply data when they are fired
connector.on('SB:::Typing', (seconds) => console.log(seconds));
connector.on('SB:::ProcessingPassed', docId => console.log(docId));
connector.on('SB:::ProcessingFailed', docId => console.log(docId));
connector.on('SB:::ValidationPassed', docId => console.log(docId));
connector.on('SB:::ValidationFailed', docId => console.log(docId));
connector.on('SB:::FileProcessorPipelinePassed', context => console.log(context));
connector.on('SB:::FileProcessorPipelineFailed', context => console.log(context));
connector.on('SB:::MessengerPopup', seconds => console.log(seconds));
connector.on('SB:::MessengerNavigate', url => console.log(url));
connector.on('SB:::MessengerStyle', data => console.log(data));

Conversation Refreshed Event

  • A conversation refreshed event occurs when the conversation's JWT auth token has expired and the conversation-runtime has successfully managed to refresh the auth tokens.
  • You can listen to this event using the following:
connector.on('ConversationRefreshed', (refreshedConversation) => {
  const newAuthToken = refreshedConversation.getAuthToken();
  const newRefreshToken = refreshedConversation.getRefreshToken();

  console.log(newRefreshToken, newRefreshToken);
})
  • The refreshedConversation passed to the callback in the event above is an instance of the Conversation class within the conversation-runtime.
  • This event gives the client an opportunity to store the new refreshed tokens, for example L2 would update local storage with the new tokens.

Conversation Expired Event

  • A conversation expired event occurs when the conversation has expired and the conversation-runtime catches the expired conversation from the ping health checks.
  • You can listen to this event using the following:
connector.on('ConversationExpired', () => {
  console.log('ConversationExpired event');
})

Error

  • For error events the connector will emit a error event, and pass the error instance to the event listener.

Other Events

  • For other events such as SecureSession etc.
  • If the runtime determines that the event is not a TimelineMessage or an Error, it will emit an event using the event's type attribute and pass the full event to the event listener.
  • For example if the runtime gets a SecureSession event, the event will look like the following
{
  "organizationId": "acme",
  "id": "aa8a6b64-2565-43ff-ab9d-007ebb0faa0b",
  "conversationId": "conv-APD491fxU7xaYWU7M-CuU",
  "identityId": "acme:::SYAGZio7T1T0_OAVEmdiC",
  "sessionId": "uZ0790y6WKvnrvTdaRm5_",
  "contentType": "state",
  "contents": {
    "state": "disabled"
  },
  "source": "server",
  "type": "SecureSession",
  "token": "SECURESESSION■conv-APD491fxU7xaYWU7M-CuU■■aa8a6b64-2565-43ff-ab9d-007ebb0faa0b"
}
  • The event's type attribute will be used to emit the event, so this will result in a SecureSession event being emitted with the payload shown above.

  • Conversation Event Handlers should be added before calling createConversation or resumeConversation on the connector. If you do not attach the handlers before calling createConversation or resumeConversation you risk missing events being emitted.

Conversation Create

  • To create a conversation you must call the createConversation function on the connector.
const parameters = { engagementAdapter: 'L2', engagementAdapterVersion: '1.2.3'};
const conversation = await connector.createConversation(parameters);
  • Any parameters that are passed to the createConversation function will be passed along to the channel which is being used.
  • The response from initialising the conversation is a Conversation instance - see the class for more details.
  • Once the create call completes a conversation is created, and a websocket connection is established to listen for events related to the conversation.
  • Once a websocket connection has successfully opened, the runtime will make a request to the ReadyForConversation on the server.
  • This will tell the server that the client is ready and listening for events on the websocket, and the server can proceed and send a new conversation or conversation resumed event into the system.

NOTE

  • A connector may only interact with one conversation at a time.
  • If a connector already contains an initialised conversation and a call to createConversation is made, an error will be thrown.
import { ConversationErrorCodes } from '@servisbot/conversation-runtime';

try {
  const parameters = { engagementAdapter: 'L2', engagementAdapterVersion: '1.2.3'};
  await connector.createConversation(parameters);

  // second call to "createConversation" will throw as the connector already has a conversation.
  await connector.createConversation(parameters);
} catch(err) {
  if (err.code === ConversationErrorCodes.CONVERSATION_ALREADY_INITIALISED) {
    console.log(err.message);
  }

  // unexpected error has occurred
  throw err;
}
  • If you want to create multiple conversations you must create a new ConversationConnector and create the conversation using the new connector.

Conversation Resume

  • To continue or resume an existing conversation you must call the resumeConversation function on the connector.
import { Conversation } from '@servisbot/conversation-runtime';

const conversation = new Conversation({
  authToken: 'some-token',
  refreshToken: 'refresh-token',
  conversationId: 'some-conversation-id',
  hasActivity: true
});

const parameters = {
  engagementAdapter: 'L2', 
  engagementAdapterVersion: '1.2.3',
  conversation
};

const conversationResumeResponse = await connector.resumeConversation(parameters);
  • Any parameters that are passed to the resumeConversation function will be passed along to the channel which is being used.
  • Once the resumeConversation call completes, a websocket connection is established to listen for events related to the connector.
  • Once a websocket connection has successfully opened, the conversation runtime will make a request to the ReadyForConversation endpoint.
  • This will inform the server that the client is ready and listening for events on the websocket, and the server can proceed and send a new conversation or conversation resumed event into the system.

A ConversationError can be thrown from a resumeConversation call in the following cases:

  • If the conversation has expired.
  • The auth tokens for the conversation are no longer valid and can not be refreshed

A ConversationError will be thrown with a code and a message. The code can be inspected within a try/catch using the ConversationErrorCodes.

import { ConversationErrorCodes } from '@servisbot/conversation-runtime';

try {
  const conversationResumeResponse = await connector.resumeConversation(parameters);
} catch (_err) {
  if (_err.code === ConversationErrorCodes.CONVERSATION_EXPIRED) {
    console.log("Conversation has expired");
  }

  if(_err.code === ConversationErrorCodes.UNAUTHORIZED) {
    console.log("Auth tokens are invalid")
  }

  // unexpected error has occurred
  return {};
}

Retry Behaviour

  • Conversation operations (create/resume/create event) use network retries to retry operations against a conversation in the event an operation fails due to a network failure.
  • If all retry attempts are exhausted then an Error will be thrown.

Send Events

You can send events by using the send function on the conversation connector. Depending on the runtime mode your running in, the responses will be in one of two places.

For OnDemandChannelRestWs, once an event is sent successfully it will be emitted on the conversation event emitter so that clients can acknowledge that an event was sent successfully.

For OnDemandChannelRestSync, once an event is sent successfully you will get a response, this response will contain any response messages from your the bot triggered by the message that was sent.

const sendResult = await conversationConnector.send(message);
// calling getMessages() will get return the response messages from the bot if there are any.
sendResult.getMessages().forEach(message => {
    // calling getRawData() will give you the raw message payload
  console.log('MESSAGE', message.getRawData())
})
  • All events can take the following attributes:
    • id - the event id, defaults to a v4 uuid if the provided value is not a string or is not a valid v4 uuid.
    • correlationId - the correlation id used to trace the event through the system, defaults to a v4 uuid if the provided value is not a string or is not a valid v4 uuid.

Send Message

import { IngressEventFactory } from '@servisbot/conversation-runtime';

const id = uuidV4();  // if not provided it will default to a v4 uuid
const correlationId = uuidV4(); // if not provided it will default to a v4 uuid
const message = 'hello world';

const message = IngressEventFactory.createMessage({ message, id, correlationId });
await connector.send(message);

Send Markup Interaction

import { IngressEventFactory } from '@servisbot/conversation-runtime';

const id = uuidV4();  // if not provided it will default to a v4 uuid
const correlationId = uuidV4(); // if not provided it will default to a v4 uuid

const source = {
  eventType: 'TimelineMessage',
  timestamp: 1647959518700,
  id: 'jmfuvLOVT-',
  conversationId: 'conv-kTKya6Oarb8lw2qUAIQae'
};

const interaction = {
  action: 'listItemInteraction',
  value: { id: '1', title: 'Item one' },
  timestamp: 1647959522136
};

const markupInteractionEvent = IngressEventFactory.createMarkupInteraction({
  source, interaction, id, correlationId
});

await connector.send(markupInteractionEvent);

Send Page Event

import { IngressEventFactory } from '@servisbot/conversation-runtime';

const id = uuidV4();  // if not provided it will default to a v4 uuid
const correlationId = uuidV4(); // if not provided it will default to a v4 uuid

const body = { // optional
  name: 'test-user'
};

const alias = 'page-event-alias';

const pageEvent = IngressEventFactory.createPageEvent({
  alias, body, id, correlationId
});

await connector.send(pageEvent);

Vend User Document

import { VendUserDocumentRequest } from '@servisbot/conversation-runtime';

const params = { 
  documentLabel: 'my-document', // required
  metadata: { FileType: 'png', CustomerReference: 'cust-123' }, // optional, defaults to {}
  additionalPathwayPath: '/some/additional/path' // optional
}

const vendUserDocumentRequest = new VendUserDocumentRequest(params);
const secureFileUploadManager = connector.getSecureFileUploadManager();

const response = await secureFileUploadManager.vendUserDocument(vendUserDocumentRequest);

const { url: urlForUploadingDocument, docId } = response;

console.log('Url for Uploading Document', url);
console.log('Document Id:', docId);

Create Secure Input

import { CreateSecureInputRequest } from '@servisbot/conversation-runtime';

const params = { 
  enigmaUrl: 'https://enigma.com',
  jwt: 'some-jwt-token',
  input: 'this is some sensitive input',
  ttl: 124242 // optional, time to live in seconds
}

const createSecureInputRequest = new CreateSecureInputRequest(params);
const secureInputManager = connector.getSecureInputManager();

const secureInputResponse = await secureInputManager.createSecureInput(createSecureInputRequest);

if(secureInputResponse.isOk()) {
  const secureInputSrn = secureInputResponse.getSrn();
  console.log(`Secure input srn: ${secureInputSrn}`);
} else {
  const {message: errMessage} = secureInputResponse.getBody(); 
  console.log(`${errMessage} with status code ${secureInputResponse.getStatusCode()}`);
}

Create Impression

  • To create an impression you can use the createImpression function via the web client manager which can be retrieved via the getWebClientManager function on the conversation connector instance.
  • There is no need to have an ongoing conversation to create an impression.
const webClientManager = connector.getWebClientManager();

const impressionId = 'some-impression-id';

await webClientManager.createImpression({ impressionId });
  • It is possible to associate metadata with the createImpression call by suppliying an instance of WebClientMetadata to the createImpression function call.
  • To construct a WebClientMetadata instance, a WebClientMetadataBuilder can be used.
  • It is recommended to use a WebClientMetadataBuilder to ensure you construct valid metadata for the createImpression function call.
  • If a createImpression is invoked with an invalid WebClientMetadata the data will not be passed along in the request to the server.
  • The following metadata can be included when creating a WebClientMetadata instance using the WebClientMetadataBuilder .
    • messengerState - the current state of the messenger. Possible values are open or closed. The MessengerStates enum can be used to specify the state to be used when constructing the WebClientMetadata.
  • The following example shows how to invoke createImpression with an instance of WebClientMetadata.
import { WebClientMetadataBuilder, MessengerStates } from '@servisbot/conversation-runtime';

const webClientMetadata = new WebClientMetadataBuilder()
  .withMessengerState(MessengerStates.OPEN)
  .build();

const webClientManager = connector.getWebClientManager();

const impressionId = 'some-impression-id';

await webClientManager.createImpression({ impressionId, webClientMetadata });

Create Transcript

  • To create a transcript you can use the createTranscript function via the venus connector. It will use the current conversation and identity to request a transcript be generated and return a signed get url to that transcript.
const transcript = await connector.createTranscript();

Get Conversation

  • To get the conversation id for a conversation after calling createConversation you can do the following
const conversation = connector.getConversation();
const conversationId = conversation.getId();

Close Connection

  • To close the websocket connection you can call the close function on the conversation
  • You can pass an object to the close function to make it available inside the runtime.
await connector.close();

Reconnection Behaviour

  • The conversation-runtime will automatically attempt to reconnect to the websocket in the following cases:
    • If the socket receives a "close" event from the server.
    • If the initial connection to the server errors.
  • If the runtime can establish a connection again, everything resumes as usual, and a reconnect success event is sent to the client.
  • If the runtime hits the max amount of reconnect retries, a failure event is sent up to the client.

Reconnection Events

  • reconnecting - this event is emitted to the client when the runtime receives a "close" event from the server, and the runtime is about to start attempting to reconnect to the web socket.
  • reconnectSuccess - this event is emitted to the client when the runtime is in a reconnecting state, and manages to establish a successful "open" event from a new web socket connection.
  • reconnectFailed - this event is emitted to the client when the runtime is in a reconnecting state, and hits the maximum number of reconnect attempts allowed.
  • To listen to these events you can attach a listener on the connector like so:
connector.on('reconnecting', () => console.log('Reconnecting'));
connector.on('reconnectFailed', () => console.log('Reconnecting Failed'));
connector.on('reconnectSuccess', () => console.log('Reconnecting Success'));

Reconnection Options

  • To configure the reconnection options you can supply WebSocketOptions to the create/resume functions on the conversation connector, see below for an example:
import { WebSocketOptions } from '@servisbot/conversation-runtime';

const webSocketOptions = new WebSocketOptions({
  maxReconnectAttempts: 3,
  baseReconnectDelay: 500,
  establishConnectionTimeout: 1000
});
const parameters = { engagementAdapter: 'L2', engagementAdapterVersion: '1.2.3', webSocketOptions};
const conversation = await connector.createConversation(parameters);
  • The following reconnect parameters can be passed as options to the WebSocketOptions class.
    • reconnectEnabled - a boolean to toggle the reconnect behaviour on/off. Defaults to true, it is recommended to leave this as true for reliability reasons.
    • maxReconnectAttempts - an integer representing the maximum amount of reconnect attempts that should be made before emitting the reconnectFailed event. This excludes the initial connection attempt.
    • baseReconnectDelay - The base number of milliseconds to use in the exponential back off for reconnect attempts. Defaults to 100 ms.
    • establishConnectionTimeout - an integer in milliseconds representing the maximum amount of time to wait for the websocket to receive an "open" event from the server before considering the connection attempt a failure.
      • This value will have an affect on the baseReconnectDelay value. Take the following example.
      • If the baseReconnectDelay is set to 20 milliseconds and the maxReconnectAttempts is set to 3 and the establishConnectionTimeout is set to 100 milliseconds, and we exhaust all retries, the timings will be as follows:
        • First reconnect attempt will occur after 20 milliseconds, we will then wait 100 before considering this reconnect attempt a failure.
        • Second reconnect attempt will occur 40 milliseconds after considering the first attempt a failure, we will then wait another 100 milliseconds before considering this attempt a failure.
        • Third reconnect attempt will occur 80 milliseconds after considering the second attempt a failure, we will then wait another 100 milliseconds before considering this attempt a failure, we will then emit a reconnectFailed error as we have hit the max reconnect attempts limit.
        • The total time spent trying to get a connection in the above example is 440 milliseconds.
        • NOTE the above example does not take into account that the exponential back off used within the conversation-runtime is based on the "Full Jitter" algorithm found which is explained here, and it is unlikely the delays will ever be in even increasing increments. Fixed delays of 20, 40 and 80 were used to simplify the example above.
    • maxFailedHealthChecks - an integer representing the maximum number of health check failures in a row before considering the web socket unhealthy
    • maxHealthCheckResponseTime - an integer in milliseconds representing the maximum amount of time to wait for a "pong" response after sending a "ping" request before considering the health check a failure.
    • healthCheckInterval - an integer in milliseconds representing how long to wait between health checks.

Reconnection Default Options

  • If no WebSocketOptions are supplied the following defaults will be used:
    • reconnectEnabled - true
    • maxReconnectAttempts - 3
    • baseReconnectDelay - 100 (milliseconds)
    • establishConnectionTimeout - 3000 (milliseconds)
    • maxFailedHealthChecks - 3
    • maxHealthCheckResponseTime - 500 (milliseconds)
    • healthCheckInterval - 5000 (milliseconds) - 5 seconds

Reconnect "Full Jitter" Back Off

  • The conversation-runtime uses the "Full Jitter" algorithm found in this blog https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/
  • This max amount of time a client will wait before attempting a reconnect is 3 seconds.
  • The minimum amount of time a client will wait before attempting a reconnect is 100 milliseconds.

Health Checking

  • Once a websocket connection is in an "open" state, conversation-runtime will continuously health check the web socket
  • Health checks are done by sending a HTTP request for a specific conversation id to the server, the server then sends the same ping id back down the web socket
  • If a HTTP ping request fails which results in a pong response not being returned on the web socket this will count as a failed health check.
  • If a response for a ping takes too long or does not arrive, it will count as a failed health check.
  • Once a websocket connection gets a "close" event, the health checking is stopped as the socket is no longer open.
  • If the number of failed health checks in a row breaches the maxFailedHealthChecks option, the conversation-runtime will attempt to get a new web socket connection by starting the reconnect process.

Synchronous mode

When using synchronous, all request are made synchronously. Each time you create a conversation or send in an event it will wait for the response including any messages related to that event. These are returned from the function call.

For more information on the event handlers you can register see here

Note that host notifications and messages will come back in the response from both the send() and createConversation() calls.

import { ConversationConnector, ConversationChannelTypes, IngressEventFactory } from '@servisbot/conversation-runtime';

const organization = 'acme';

const apiKey = '<servisbot-conversation-api-key>'

const endpointAddress = 'acme-MyBot';

// can be "eu-1", "us1" or "usccif1"
const sbRegion = 'us1';
// When using sync mode the connector should use the `OnDemandChannelRestSync` channel type from the ConversationChannelTypes
const conversationConnector = await ConversationConnector.createConnector({
  channelType: ConversationChannelTypes.OnDemandChannelRestSync,
  apiKey,
  endpointAddress,
  logger,
  sbRegion,
  organization,
});

// Setup conversation event listeners before creating the conversation
conversationConnector.on('ConversationRefreshed', (msg) => console.log('ConversationRefreshed', msg));

const conversation = await conversationConnector.createConversation({
  engagementAdapter: 'L2', engagementAdapterVersion: '1.0.0'
});

// When a conversation is created using the synchronous channel, the conversation can contain welcome messages as part of the conversation instance
// You can call getWelcomeMessages() to get any messages from the result
const  messages  = conversation.getWelcomeMessages();
messages.forEach(message => {
  // calling getRawData() will give you the raw message payload
  console.log('MESSAGE', message.getRawData())
})
const message = IngressEventFactory.createMessage({ message: 'content' })

const sendResult = await conversationConnector.send(message);
// calling getMessages() will get any messages from response
sendResult.getMessages().forEach(message => {
    // calling getRawData() will give you the raw message payload
  console.log('MESSAGE', message.getRawData())
})

Limitations of Synchronous mode

There is no continuous health checking (pinging) to the server when using the synchronous channel. This means the detection of a conversation expiring will not occur until the connector attempts to send a message or resume a conversation.

An error will be thrown if the server determines that the conversation has expired in both of the above cases.