@microsoft/omnichannel-chat-widget 1.8.2-main.fc93d3d → 1.8.3-main.3445895

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/README.md +46 -1
  2. package/lib/cjs/common/Constants.js +8 -2
  3. package/lib/cjs/common/telemetry/TelemetryConstants.js +3 -0
  4. package/lib/cjs/common/telemetry/TelemetryHelper.js +7 -5
  5. package/lib/cjs/common/utils.js +27 -2
  6. package/lib/cjs/components/chatbuttonstateful/ChatButtonStateful.js +4 -4
  7. package/lib/cjs/components/livechatwidget/common/createInternetConnectionChangeHandler.js +22 -9
  8. package/lib/cjs/components/livechatwidget/common/createMarkdown.js +54 -1
  9. package/lib/cjs/components/livechatwidget/common/customEventHandler.js +53 -0
  10. package/lib/cjs/components/livechatwidget/common/endChat.js +13 -2
  11. package/lib/cjs/components/livechatwidget/common/initWebChatComposer.js +8 -3
  12. package/lib/cjs/components/livechatwidget/common/renderSurveyHelpers.js +23 -0
  13. package/lib/cjs/components/livechatwidget/common/startChat.js +5 -4
  14. package/lib/cjs/components/livechatwidget/livechatwidgetstateful/LiveChatWidgetStateful.js +16 -6
  15. package/lib/cjs/components/webchatcontainerstateful/WebChatContainerStateful.js +1 -2
  16. package/lib/cjs/components/webchatcontainerstateful/common/DesignerChatAdapter.js +3 -1
  17. package/lib/cjs/components/webchatcontainerstateful/common/utils/chatAdapterUtils.js +27 -2
  18. package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/callActionMiddleware.js +42 -0
  19. package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/citationsMiddleware.js +72 -0
  20. package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/customEventMiddleware.js +41 -0
  21. package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/queueOverflowHandlerMiddleware.js +45 -0
  22. package/lib/cjs/contexts/common/CustomEventType.js +1 -0
  23. package/lib/cjs/firstresponselatency/FirstMessageTrackerFromBot.js +101 -36
  24. package/lib/cjs/firstresponselatency/FirstResponseLatencyTracker.js +39 -21
  25. package/lib/cjs/firstresponselatency/util.js +24 -10
  26. package/lib/cjs/plugins/createChatTranscript.js +13 -0
  27. package/lib/cjs/plugins/newMessageEventHandler.js +2 -2
  28. package/lib/esm/common/Constants.js +8 -2
  29. package/lib/esm/common/telemetry/TelemetryConstants.js +3 -0
  30. package/lib/esm/common/telemetry/TelemetryHelper.js +7 -5
  31. package/lib/esm/common/utils.js +21 -0
  32. package/lib/esm/components/chatbuttonstateful/ChatButtonStateful.js +4 -4
  33. package/lib/esm/components/livechatwidget/common/createInternetConnectionChangeHandler.js +22 -9
  34. package/lib/esm/components/livechatwidget/common/createMarkdown.js +54 -1
  35. package/lib/esm/components/livechatwidget/common/customEventHandler.js +45 -0
  36. package/lib/esm/components/livechatwidget/common/endChat.js +13 -2
  37. package/lib/esm/components/livechatwidget/common/initWebChatComposer.js +9 -4
  38. package/lib/esm/components/livechatwidget/common/renderSurveyHelpers.js +23 -0
  39. package/lib/esm/components/livechatwidget/common/startChat.js +5 -4
  40. package/lib/esm/components/livechatwidget/livechatwidgetstateful/LiveChatWidgetStateful.js +16 -6
  41. package/lib/esm/components/webchatcontainerstateful/WebChatContainerStateful.js +1 -2
  42. package/lib/esm/components/webchatcontainerstateful/common/DesignerChatAdapter.js +4 -2
  43. package/lib/esm/components/webchatcontainerstateful/common/utils/chatAdapterUtils.js +23 -0
  44. package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/callActionMiddleware.js +36 -0
  45. package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/citationsMiddleware.js +65 -0
  46. package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/customEventMiddleware.js +33 -0
  47. package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/queueOverflowHandlerMiddleware.js +38 -0
  48. package/lib/esm/contexts/common/CustomEventType.js +1 -0
  49. package/lib/esm/firstresponselatency/FirstMessageTrackerFromBot.js +101 -36
  50. package/lib/esm/firstresponselatency/FirstResponseLatencyTracker.js +39 -21
  51. package/lib/esm/firstresponselatency/util.js +21 -8
  52. package/lib/esm/plugins/createChatTranscript.js +13 -0
  53. package/lib/esm/plugins/newMessageEventHandler.js +3 -3
  54. package/lib/types/common/Constants.d.ts +8 -2
  55. package/lib/types/common/telemetry/TelemetryConstants.d.ts +3 -0
  56. package/lib/types/common/utils.d.ts +8 -0
  57. package/lib/types/components/livechatwidget/common/customEventHandler.d.ts +4 -0
  58. package/lib/types/components/webchatcontainerstateful/common/utils/chatAdapterUtils.d.ts +2 -0
  59. package/lib/types/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/callActionMiddleware.d.ts +8 -0
  60. package/lib/types/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/citationsMiddleware.d.ts +5 -0
  61. package/lib/types/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/customEventMiddleware.d.ts +22 -0
  62. package/lib/types/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/queueOverflowHandlerMiddleware.d.ts +5 -0
  63. package/lib/types/contexts/common/CustomEventType.d.ts +6 -0
  64. package/lib/types/firstresponselatency/FirstResponseLatencyTracker.d.ts +2 -2
  65. package/lib/types/firstresponselatency/util.d.ts +1 -0
  66. package/package.json +5 -4
@@ -17,14 +17,13 @@ let FirstResponseLatencyTracker = /*#__PURE__*/function () {
17
17
  function FirstResponseLatencyTracker() {
18
18
  _classCallCheck(this, FirstResponseLatencyTracker);
19
19
  _defineProperty(this, "isABotConversation", false);
20
- _defineProperty(this, "isStarted", false);
21
- _defineProperty(this, "isEnded", false);
20
+ _defineProperty(this, "isTracking", false);
22
21
  _defineProperty(this, "startTrackingMessage", void 0);
23
22
  _defineProperty(this, "stopTrackingMessage", void 0);
24
23
  _defineProperty(this, "isReady", false);
24
+ _defineProperty(this, "trackingTimeoutId", void 0);
25
25
  _defineProperty(this, "offlineNetworkListener", _omnichannelChatComponents.BroadcastService.getMessageByEventName(_TelemetryConstants.TelemetryEvent.NetworkDisconnected).subscribe(() => {
26
- this.isStarted = false;
27
- this.isEnded = false;
26
+ this.isTracking = false;
28
27
  _TelemetryHelper.TelemetryHelper.logActionEvent(_TelemetryConstants.LogLevel.INFO, {
29
28
  Event: _TelemetryConstants.TelemetryEvent.MessageStopLapTrackError,
30
29
  Description: "Tracker Stopped due to network disconnection"
@@ -72,7 +71,7 @@ let FirstResponseLatencyTracker = /*#__PURE__*/function () {
72
71
  value: function startTracking(payload) {
73
72
  if (!this.isReady) return;
74
73
  // this prevents to initiate tracking for multiple incoming messages
75
- if (this.isStarted) {
74
+ if (this.isTracking) {
76
75
  return;
77
76
  }
78
77
  // this is to ensure we track only messages where bot is engaged
@@ -80,10 +79,24 @@ let FirstResponseLatencyTracker = /*#__PURE__*/function () {
80
79
  return;
81
80
  }
82
81
  // control of states to prevent clashing of messages
83
- this.isStarted = true;
84
- this.isEnded = false;
82
+ this.isTracking = true;
85
83
  // The idea of using types is to enrich telemetry data
86
84
  this.startTrackingMessage = this.createTrackingMessage(payload, "userMessage");
85
+
86
+ // Start a 5-second timeout to auto-stop tracking if not stopped
87
+ if (this.trackingTimeoutId) {
88
+ clearTimeout(this.trackingTimeoutId);
89
+ }
90
+ this.trackingTimeoutId = setTimeout(() => {
91
+ // this means the start process is in progress, but the end wasn't called within the time limit
92
+ if (this.isTracking) {
93
+ // Reset state variables and skip stopTracking
94
+ this.isTracking = false;
95
+ this.startTrackingMessage = undefined;
96
+ this.stopTrackingMessage = undefined;
97
+ this.trackingTimeoutId = undefined;
98
+ }
99
+ }, 5000);
87
100
  }
88
101
  }, {
89
102
  key: "handleAgentMessage",
@@ -99,13 +112,16 @@ let FirstResponseLatencyTracker = /*#__PURE__*/function () {
99
112
  value: function stopTracking(payload) {
100
113
  var _this$stopTrackingMes, _this$startTrackingMe;
101
114
  // this prevents execution for multiple incoming messages from the bot.
102
- if (this.isEnded && !this.isStarted) {
115
+ if (!this.isTracking) {
103
116
  return;
104
117
  }
105
-
118
+ // Clear the timeout if it exists
119
+ if (this.trackingTimeoutId) {
120
+ clearTimeout(this.trackingTimeoutId);
121
+ this.trackingTimeoutId = undefined;
122
+ }
106
123
  // control of states to prevent clashing of messages
107
- this.isEnded = true;
108
- this.isStarted = false;
124
+ this.isTracking = false;
109
125
 
110
126
  // The idea of using types is to enrich telemetry data
111
127
  this.stopTrackingMessage = this.createTrackingMessage(payload, "botMessage");
@@ -160,12 +176,16 @@ let FirstResponseLatencyTracker = /*#__PURE__*/function () {
160
176
  if (!payload || !payload.Id) {
161
177
  throw new Error("Invalid payload");
162
178
  }
163
- if (!this.isMessageFromValidSender(payload)) return;
164
- if (this.isABotConversation && this.isStarted) {
179
+
180
+ // Only allow stopTracking if sender is valid and tracking is active
181
+ if (!this.isMessageFromValidSender(payload)) {
182
+ // Do not change isTracking or stopTrackingMessage
183
+ return;
184
+ }
185
+ if (this.isABotConversation && this.isTracking) {
165
186
  this.stopTracking(payload);
166
187
  }
167
188
  } catch (e) {
168
- console.error("FRL : error while trying to stop the tracker", e);
169
189
  _TelemetryHelper.TelemetryHelper.logActionEvent(_TelemetryConstants.LogLevel.ERROR, {
170
190
  Event: _TelemetryConstants.TelemetryEvent.MessageStopLapTrackError,
171
191
  Description: "Error while stopping the clock",
@@ -174,11 +194,6 @@ let FirstResponseLatencyTracker = /*#__PURE__*/function () {
174
194
  payload: payload
175
195
  }
176
196
  });
177
- //reset state
178
- this.startTrackingMessage = undefined;
179
- this.stopTrackingMessage = undefined;
180
- this.isStarted = false;
181
- this.isEnded = false;
182
197
  }
183
198
  }
184
199
  }, {
@@ -186,10 +201,13 @@ let FirstResponseLatencyTracker = /*#__PURE__*/function () {
186
201
  value: function deregister() {
187
202
  // Reset State
188
203
  this.isABotConversation = false;
189
- this.isStarted = false;
190
- this.isEnded = false;
204
+ this.isTracking = false;
191
205
  this.startTrackingMessage = undefined;
192
206
  this.stopTrackingMessage = undefined;
207
+ if (this.trackingTimeoutId) {
208
+ clearTimeout(this.trackingTimeoutId);
209
+ this.trackingTimeoutId = undefined;
210
+ }
193
211
  this.offlineNetworkListener.unsubscribe();
194
212
  this.fmltrackingListener.unsubscribe();
195
213
  this.rehydrateListener.unsubscribe();
@@ -3,9 +3,11 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.polyfillMessagePayloadForEvent = exports.isHistoryMessage = exports.getScenarioType = exports.extractTimestampFromId = exports.createTrackingMessage = exports.buildMessagePayload = void 0;
6
+ exports.polyfillMessagePayloadForEvent = exports.maskPayloadText = exports.isHistoryMessage = exports.getScenarioType = exports.extractTimestampFromId = exports.createTrackingMessage = exports.buildMessagePayload = void 0;
7
7
  var _Constants = require("./Constants");
8
8
  var _Constants2 = require("../common/Constants");
9
+ const DELTA_WITHIN_LIMITS_IN_MS = 250;
10
+
9
11
  /**
10
12
  * Determines whether a given activity is a historical message.
11
13
  *
@@ -23,19 +25,21 @@ var _Constants2 = require("../common/Constants");
23
25
  * - If the ID is valid and the timestamp is older than the start time, the message is historical.
24
26
  */
25
27
  const isHistoryMessage = (activity, startTime) => {
26
- var _activity$channelData, _activity$channelData2;
28
+ var _activity$channelData;
27
29
  // Only process message activities
28
30
  if ((activity === null || activity === void 0 ? void 0 : activity.type) !== _Constants2.Constants.message) {
29
31
  return false;
30
32
  }
31
33
 
32
- // Legacy check for history message tag
33
- if (activity !== null && activity !== void 0 && (_activity$channelData = activity.channelData) !== null && _activity$channelData !== void 0 && (_activity$channelData2 = _activity$channelData.tags) !== null && _activity$channelData2 !== void 0 && _activity$channelData2.includes(_Constants2.Constants.historyMessageTag)) {
34
+ // Prioritize legacy history tag
35
+ if (activity !== null && activity !== void 0 && (_activity$channelData = activity.channelData) !== null && _activity$channelData !== void 0 && _activity$channelData.tags && activity.channelData.tags.includes(_Constants2.Constants.historyMessageTag)) {
34
36
  return true;
35
37
  }
36
38
  const activityId = extractTimestampFromId(activity);
37
39
  const isValidId = !isNaN(activityId) && activityId > 0;
38
- const isOlderThanStartTime = activityId < startTime;
40
+ const difference = startTime - activityId;
41
+ // Only consider historical if activityId < startTime and difference >= DELTA_WITHIN_LIMITS_IN_MS
42
+ const isOlderThanStartTime = activityId < startTime && difference >= DELTA_WITHIN_LIMITS_IN_MS;
39
43
  const isHistoryById = isValidId && isOlderThanStartTime;
40
44
  return isHistoryById;
41
45
  };
@@ -64,7 +68,7 @@ const extractTimestampFromId = activity => {
64
68
  };
65
69
  exports.extractTimestampFromId = extractTimestampFromId;
66
70
  const buildMessagePayload = (activity, userId) => {
67
- var _text, _text2, _activity$channelData3, _activity$from;
71
+ var _text, _text2, _activity$channelData2, _activity$from;
68
72
  return {
69
73
  // To identify hidden contents vs empty content
70
74
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -72,7 +76,7 @@ const buildMessagePayload = (activity, userId) => {
72
76
  type: activity === null || activity === void 0 ? void 0 : activity.type,
73
77
  timestamp: activity === null || activity === void 0 ? void 0 : activity.timestamp,
74
78
  userId: userId,
75
- tags: activity === null || activity === void 0 ? void 0 : (_activity$channelData3 = activity.channelData) === null || _activity$channelData3 === void 0 ? void 0 : _activity$channelData3.tags,
79
+ tags: (activity === null || activity === void 0 ? void 0 : (_activity$channelData2 = activity.channelData) === null || _activity$channelData2 === void 0 ? void 0 : _activity$channelData2.tags) || [],
76
80
  messageType: "",
77
81
  Id: activity === null || activity === void 0 ? void 0 : activity.id,
78
82
  role: activity === null || activity === void 0 ? void 0 : (_activity$from = activity.from) === null || _activity$from === void 0 ? void 0 : _activity$from.role,
@@ -98,9 +102,9 @@ const polyfillMessagePayloadForEvent = (activity, payload, conversationId) => {
98
102
  };
99
103
  exports.polyfillMessagePayloadForEvent = polyfillMessagePayloadForEvent;
100
104
  const getScenarioType = activity => {
101
- var _activity$from3, _activity$channelData4;
105
+ var _activity$from3, _activity$channelData3;
102
106
  const role = activity === null || activity === void 0 ? void 0 : (_activity$from3 = activity.from) === null || _activity$from3 === void 0 ? void 0 : _activity$from3.role;
103
- const tags = activity === null || activity === void 0 ? void 0 : (_activity$channelData4 = activity.channelData) === null || _activity$channelData4 === void 0 ? void 0 : _activity$channelData4.tags;
107
+ const tags = activity === null || activity === void 0 ? void 0 : (_activity$channelData3 = activity.channelData) === null || _activity$channelData3 === void 0 ? void 0 : _activity$channelData3.tags;
104
108
  if (role === _Constants2.Constants.userMessageTag) {
105
109
  return _Constants.ScenarioType.UserSendMessageStrategy;
106
110
  }
@@ -122,4 +126,14 @@ const createTrackingMessage = (payload, type) => {
122
126
  checkTime: new Date().getTime()
123
127
  };
124
128
  };
125
- exports.createTrackingMessage = createTrackingMessage;
129
+ exports.createTrackingMessage = createTrackingMessage;
130
+ const maskPayloadText = payload => {
131
+ if (!payload) {
132
+ return payload;
133
+ }
134
+ return {
135
+ ...payload,
136
+ text: "*contents hidden*"
137
+ };
138
+ };
139
+ exports.maskPayloadText = maskPayloadText;
@@ -705,10 +705,23 @@ const createChatTranscript = async function (transcript, facadeChatSDK) {
705
705
  reader.readAsDataURL(blob);
706
706
  });
707
707
  };
708
+
709
+ // Configure DOMPurify to remove target attribute from br tags
710
+ const hook = function (node) {
711
+ // eslint-disable-line @typescript-eslint/no-explicit-any
712
+ // Remove target attribute from br tags as it causes them to display as literal text
713
+ if (node.tagName === "BR" && node.hasAttribute("target")) {
714
+ node.removeAttribute("target");
715
+ }
716
+ };
717
+ _dompurify.default.addHook("afterSanitizeAttributes", hook);
708
718
  let messages = transcriptMessages.filter(message => {
709
719
  message.content = _dompurify.default.sanitize(message.content);
710
720
  return message;
711
721
  });
722
+
723
+ // Clean up the hook after processing all messages
724
+ _dompurify.default.removeHook("afterSanitizeAttributes", hook);
712
725
  if (renderAttachments) {
713
726
  messages = await Promise.all(transcriptMessages.map(async message => {
714
727
  // eslint-disable-line @typescript-eslint/no-explicit-any
@@ -71,7 +71,7 @@ const createOnNewAdapterActivityHandler = (chatId, userId, startTime) => {
71
71
  _TelemetryHelper.TelemetryHelper.logActionEventToAllTelemetry(_TelemetryConstants.LogLevel.INFO, {
72
72
  Event: _TelemetryConstants.TelemetryEvent.RehydrateMessageReceived,
73
73
  Description: "History message received",
74
- CustomProperties: payload
74
+ CustomProperties: (0, _util.maskPayloadText)(payload)
75
75
  });
76
76
  }
77
77
  };
@@ -108,7 +108,7 @@ const createOnNewAdapterActivityHandler = (chatId, userId, startTime) => {
108
108
  _TelemetryHelper.TelemetryHelper.logActionEventToAllTelemetry(_TelemetryConstants.LogLevel.INFO, {
109
109
  Event: _TelemetryConstants.TelemetryEvent.MessageReceived,
110
110
  Description: "New message received",
111
- CustomProperties: payload
111
+ CustomProperties: (0, _util.maskPayloadText)(payload)
112
112
  });
113
113
  };
114
114
  const raiseMessageEvent = activity => {
@@ -25,6 +25,7 @@ _defineProperty(Constants, "webchatChannelId", "webchat");
25
25
  _defineProperty(Constants, "markdown", "markdown");
26
26
  _defineProperty(Constants, "actionType", "actionType");
27
27
  _defineProperty(Constants, "markDownSystemMessageClass", "webchat__basic-transcript__activity-markdown-body");
28
+ _defineProperty(Constants, "MARKDOWN_LIST_INDENTATION", " ");
28
29
  _defineProperty(Constants, "String", "string");
29
30
  _defineProperty(Constants, "ChatMessagesJson", "chatMessagesJson");
30
31
  _defineProperty(Constants, "truePascal", "True");
@@ -108,8 +109,7 @@ _defineProperty(Constants, "TargetRelationshipAttributes", "noopener noreferrer"
108
109
  // Markdown icons
109
110
  _defineProperty(Constants, "OpenLinkIconCssClass", "webchat__render-markdown__external-link-icon");
110
111
  // internet connection test
111
- _defineProperty(Constants, "internetConnectionTestUrl", "https://ocsdk-prod.azureedge.net/public/connecttest.txt");
112
- _defineProperty(Constants, "internetConnectionTestUrlText", "Omnichannel Connect Test");
112
+ _defineProperty(Constants, "internetConnectionTestPath", "/livechatwidget/version.txt");
113
113
  _defineProperty(Constants, "ChatWidgetStateChangedPrefix", "ChatWidgetStateChanged");
114
114
  _defineProperty(Constants, "PostChatLoadingDurationInMs", 2000);
115
115
  _defineProperty(Constants, "BrowserUnloadConfirmationMessage", "Do you want to leave chat?");
@@ -125,6 +125,12 @@ _defineProperty(Constants, "InitContextParamsResponse", "initContextParamsRespon
125
125
  _defineProperty(Constants, "OCOriginalMessageId", "OriginalMessageId");
126
126
  _defineProperty(Constants, "WebchatSequenceIdAttribute", "webchat:sequence-id");
127
127
  _defineProperty(Constants, "MessageSequenceIdOverride", "MessageSequenceIdOverride");
128
+ _defineProperty(Constants, "sendCustomEvent", "sendCustomEvent");
129
+ _defineProperty(Constants, "onCustomEvent", "onCustomEvent");
130
+ _defineProperty(Constants, "customEventName", "customEventName");
131
+ _defineProperty(Constants, "customEventValue", "customEventValue");
132
+ _defineProperty(Constants, "Hidden", "Hidden");
133
+ _defineProperty(Constants, "EndConversationDueToOverflow", "endconversationduetooverflow");
128
134
  export const Regex = (_class = /*#__PURE__*/_createClass(function Regex() {
129
135
  _classCallCheck(this, Regex);
130
136
  }), _defineProperty(_class, "EmailRegex", "^(?:[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"[^\"\\\\]*(?:\\\\.[^\"\\\\]*)*\")@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?)*|\\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\])$"), _class);
@@ -184,10 +184,12 @@ export let TelemetryEvent;
184
184
  TelemetryEvent["BotAuthActivityUndefinedSignInId"] = "BotAuthActivityUndefinedSignInId";
185
185
  TelemetryEvent["ThirdPartyCookiesBlocked"] = "ThirdPartyCookiesBlocked";
186
186
  TelemetryEvent["ParticipantsRemovedEvent"] = "ParticipantsRemovedEvent";
187
+ TelemetryEvent["QueueOverflowEvent"] = "QueueOverflowEvent";
187
188
  TelemetryEvent["ProcessingHTMLTextMiddlewareFailed"] = "ProcessingHTMLTextMiddlewareFailed";
188
189
  TelemetryEvent["ProcessingSanitizationMiddlewareFailed"] = "ProcessingSanitizationMiddlewareFailed";
189
190
  TelemetryEvent["FormatTagsMiddlewareJSONStringifyFailed"] = "FormatTagsMiddlewareJSONStringifyFailed";
190
191
  TelemetryEvent["AttachmentUploadValidatorMiddlewareFailed"] = "AttachmentUploadValidatorMiddlewareFailed";
192
+ TelemetryEvent["CitationMiddlewareFailed"] = "CitationMiddlewareFailed";
191
193
  TelemetryEvent["QueuePositionMessageRecieved"] = "QueuePositionMessageRecieved";
192
194
  TelemetryEvent["AverageWaitTimeMessageRecieved"] = "AverageWaitTimeMessageRecieved";
193
195
  TelemetryEvent["DataMaskingRuleApplied"] = "DataMaskingRuleApplied";
@@ -222,6 +224,7 @@ export let TelemetryEvent;
222
224
  TelemetryEvent["SystemMessageReceived"] = "SystemMessageReceived";
223
225
  TelemetryEvent["RehydrateMessageReceived"] = "RehydrateMessageReceived";
224
226
  TelemetryEvent["CustomContextReceived"] = "CustomContextReceived";
227
+ TelemetryEvent["CustomEventAction"] = "CustomEventAction";
225
228
  TelemetryEvent["NetworkDisconnected"] = "NetworkDisconnected";
226
229
  TelemetryEvent["NetworkReconnected"] = "NetworkReconnected";
227
230
  TelemetryEvent["LinkModePostChatWorkflowStarted"] = "LinkModePostChatWorkflowStarted";
@@ -230,11 +230,13 @@ export let TelemetryHelper = /*#__PURE__*/function () {
230
230
  }, {
231
231
  key: "postTelemetryEvent",
232
232
  value: function postTelemetryEvent(eventName, logLevel, payload) {
233
+ var _TelemetryManager$Int16;
233
234
  const telemetryEvent = {
234
235
  eventName,
235
236
  logLevel,
236
237
  payload: {
237
- ...payload
238
+ ...payload,
239
+ runtimeId: (_TelemetryManager$Int16 = TelemetryManager.InternalTelemetryData) === null || _TelemetryManager$Int16 === void 0 ? void 0 : _TelemetryManager$Int16.lcwRuntimeId
238
240
  }
239
241
  };
240
242
  BroadcastService.postMessage(telemetryEvent);
@@ -260,11 +262,11 @@ _defineProperty(TelemetryHelper, "logActionEvent", (logLevel, payload) => {
260
262
  TelemetryHelper.postTelemetryEvent((payload === null || payload === void 0 ? void 0 : payload.Event) ?? "", logLevel, payload);
261
263
  });
262
264
  _defineProperty(TelemetryHelper, "logSDKEvent", (logLevel, payload) => {
263
- var _TelemetryManager$Int16;
265
+ var _TelemetryManager$Int17;
264
266
  TelemetryHelper.postTelemetryEvent((payload === null || payload === void 0 ? void 0 : payload.Event) ?? "", logLevel, {
265
267
  ...payload,
266
268
  TransactionId: newGuid(),
267
- RequestId: (_TelemetryManager$Int16 = TelemetryManager.InternalTelemetryData) === null || _TelemetryManager$Int16 === void 0 ? void 0 : _TelemetryManager$Int16.currentRequestId
269
+ RequestId: (_TelemetryManager$Int17 = TelemetryManager.InternalTelemetryData) === null || _TelemetryManager$Int17 === void 0 ? void 0 : _TelemetryManager$Int17.currentRequestId
268
270
  });
269
271
  });
270
272
  _defineProperty(TelemetryHelper, "logConfigDataEvent", (logLevel, payload) => {
@@ -299,12 +301,12 @@ _defineProperty(TelemetryHelper, "logFacadeChatSDKEventToAllTelemetry", (logLeve
299
301
  });
300
302
  });
301
303
  _defineProperty(TelemetryHelper, "logSDKEventToAllTelemetry", (logLevel, payload) => {
302
- var _TelemetryManager$Int17;
304
+ var _TelemetryManager$Int18;
303
305
  TelemetryHelper.postTelemetryEvent((payload === null || payload === void 0 ? void 0 : payload.Event) ?? "", logLevel, {
304
306
  ...{
305
307
  ...payload,
306
308
  TransactionId: newGuid(),
307
- RequestId: (_TelemetryManager$Int17 = TelemetryManager.InternalTelemetryData) === null || _TelemetryManager$Int17 === void 0 ? void 0 : _TelemetryManager$Int17.currentRequestId
309
+ RequestId: (_TelemetryManager$Int18 = TelemetryManager.InternalTelemetryData) === null || _TelemetryManager$Int18 === void 0 ? void 0 : _TelemetryManager$Int18.currentRequestId
308
310
  },
309
311
  LogToAll: true
310
312
  });
@@ -453,4 +453,25 @@ export function getDeviceType() {
453
453
  } else {
454
454
  return "standard";
455
455
  }
456
+ }
457
+
458
+ //Bots expect a payload containing:
459
+ //1. customEventName: this should be string describe the event name
460
+ //2. customEventValue: given the value is from customer with unknown type, it is required to stringify the payload later
461
+ export const isValidCustomEvent = payload => {
462
+ if (Constants.customEventName in payload && payload.customEventName && typeof payload.customEventName === Constants.String && Constants.customEventValue in payload && payload.customEventValue) return true;
463
+ return false;
464
+ };
465
+ export const getCustomEventValue = customEventPayload => {
466
+ let returnVal = "";
467
+ try {
468
+ returnVal = typeof customEventPayload.customEventValue === Constants.String ? customEventPayload.customEventValue : JSON.stringify(customEventPayload.customEventValue);
469
+ } catch (error) {
470
+ console.error(error);
471
+ }
472
+ return returnVal;
473
+ };
474
+ export function isEndConversationDueToOverflowActivity(activity) {
475
+ var _activity$channelData, _activity$channelData2;
476
+ return (activity === null || activity === void 0 ? void 0 : (_activity$channelData = activity.channelData) === null || _activity$channelData === void 0 ? void 0 : _activity$channelData.tags) && Array.isArray(activity === null || activity === void 0 ? void 0 : (_activity$channelData2 = activity.channelData) === null || _activity$channelData2 === void 0 ? void 0 : _activity$channelData2.tags) && activity.channelData.tags.includes(Constants.EndConversationDueToOverflow);
456
477
  }
@@ -65,7 +65,6 @@ export const ChatButtonStateful = props => {
65
65
  };
66
66
  const outOfOfficeStyleProps = Object.assign({}, defaultOutOfOfficeChatButtonStyleProps, outOfOfficeButtonProps === null || outOfOfficeButtonProps === void 0 ? void 0 : outOfOfficeButtonProps.styleProps);
67
67
  const controlProps = {
68
- ...(buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.controlProps),
69
68
  id: "oc-lcw-chat-button",
70
69
  dir: state.domainStates.globalDir,
71
70
  titleText: "Let's Chat!",
@@ -74,7 +73,8 @@ export const ChatButtonStateful = props => {
74
73
  unreadMessageCount: state.appStates.unreadMessageCount ? state.appStates.unreadMessageCount > Constants.maximumUnreadMessageCount ? (_props$buttonProps = props.buttonProps) === null || _props$buttonProps === void 0 ? void 0 : (_props$buttonProps$co = _props$buttonProps.controlProps) === null || _props$buttonProps$co === void 0 ? void 0 : _props$buttonProps$co.largeUnreadMessageString : state.appStates.unreadMessageCount.toString() : "0",
75
74
  unreadMessageString: (_props$buttonProps2 = props.buttonProps) === null || _props$buttonProps2 === void 0 ? void 0 : (_props$buttonProps2$c = _props$buttonProps2.controlProps) === null || _props$buttonProps2$c === void 0 ? void 0 : _props$buttonProps2$c.unreadMessageString,
76
75
  // Regular chat button onClick - this will always take precedence
77
- onClick: () => ref.current()
76
+ onClick: () => ref.current(),
77
+ ...(buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.controlProps)
78
78
  };
79
79
  const outOfOfficeControlProps = {
80
80
  // Only take specific properties from outOfOfficeButtonProps, never onClick
@@ -83,7 +83,6 @@ export const ChatButtonStateful = props => {
83
83
  titleText: (outOfOfficeButtonProps === null || outOfOfficeButtonProps === void 0 ? void 0 : (_outOfOfficeButtonPro = outOfOfficeButtonProps.controlProps) === null || _outOfOfficeButtonPro === void 0 ? void 0 : _outOfOfficeButtonPro.titleText) || "We're Offline",
84
84
  subtitleText: (outOfOfficeButtonProps === null || outOfOfficeButtonProps === void 0 ? void 0 : (_outOfOfficeButtonPro2 = outOfOfficeButtonProps.controlProps) === null || _outOfOfficeButtonPro2 === void 0 ? void 0 : _outOfOfficeButtonPro2.subtitleText) || "No agents available",
85
85
  unreadMessageString: (_props$buttonProps3 = props.buttonProps) === null || _props$buttonProps3 === void 0 ? void 0 : (_props$buttonProps3$c = _props$buttonProps3.controlProps) === null || _props$buttonProps3$c === void 0 ? void 0 : _props$buttonProps3$c.unreadMessageString,
86
- ...(outOfOfficeButtonProps === null || outOfOfficeButtonProps === void 0 ? void 0 : outOfOfficeButtonProps.controlProps),
87
86
  // Out-of-office specific onClick - this will ALWAYS take precedence
88
87
  onClick: () => {
89
88
  if (state.appStates.isMinimized) {
@@ -96,7 +95,8 @@ export const ChatButtonStateful = props => {
96
95
  type: LiveChatWidgetActionType.SET_CONVERSATION_STATE,
97
96
  payload: ConversationState.OutOfOffice
98
97
  });
99
- }
98
+ },
99
+ ...(outOfOfficeButtonProps === null || outOfOfficeButtonProps === void 0 ? void 0 : outOfOfficeButtonProps.controlProps)
100
100
  };
101
101
  useEffect(() => {
102
102
  TelemetryHelper.logLoadingEvent(LogLevel.INFO, {
@@ -7,34 +7,47 @@ import { TelemetryHelper } from "../../../common/telemetry/TelemetryHelper";
7
7
  import { defaultMiddlewareLocalizedTexts } from "../../webchatcontainerstateful/common/defaultProps/defaultMiddlewareLocalizedTexts";
8
8
  import { executeReducer } from "../../../contexts/createReducer";
9
9
  import { LiveChatWidgetActionType } from "../../../contexts/common/LiveChatWidgetActionType";
10
- const isInternetConnected = async () => {
10
+ const getRegionBasedInternetTestUrl = widgetSnippet => {
11
+ var _widgetSnippet$match;
12
+ if (!widgetSnippet) {
13
+ return null;
14
+ }
15
+ const widgetSnippetSourceRegex = new RegExp("src=\"(https:\\/\\/[\\w-.]+)[\\w-.\\/]+\"");
16
+ const baseCdnUrl = (_widgetSnippet$match = widgetSnippet.match(widgetSnippetSourceRegex)) === null || _widgetSnippet$match === void 0 ? void 0 : _widgetSnippet$match[1];
17
+ return baseCdnUrl ? `${baseCdnUrl}${Constants.internetConnectionTestPath}` : null;
18
+ };
19
+ const isInternetConnected = async testUrl => {
11
20
  try {
12
- const response = await fetch(Constants.internetConnectionTestUrl);
13
- const text = await response.text();
14
- return text === Constants.internetConnectionTestUrlText;
21
+ const response = await fetch(testUrl, {
22
+ method: "GET",
23
+ cache: "no-cache"
24
+ });
25
+ return response.ok;
15
26
  } catch {
16
27
  return false;
17
28
  }
18
29
  };
19
30
  export const createInternetConnectionChangeHandler = async state => {
20
31
  const handler = async () => {
21
- const connected = await isInternetConnected();
32
+ var _inMemoryState$domain, _inMemoryState$domain2;
22
33
  const inMemoryState = executeReducer(state, {
23
34
  type: LiveChatWidgetActionType.GET_IN_MEMORY_STATE,
24
35
  payload: null
25
36
  });
37
+ const testUrl = getRegionBasedInternetTestUrl((_inMemoryState$domain = inMemoryState.domainStates.liveChatConfig) === null || _inMemoryState$domain === void 0 ? void 0 : (_inMemoryState$domain2 = _inMemoryState$domain.LiveWSAndLiveChatEngJoin) === null || _inMemoryState$domain2 === void 0 ? void 0 : _inMemoryState$domain2.msdyn_widgetsnippet);
38
+ const connected = testUrl ? await isInternetConnected(testUrl) : false;
26
39
  if (!connected) {
27
- var _inMemoryState$domain, _inMemoryState$domain2;
40
+ var _inMemoryState$domain3, _inMemoryState$domain4;
28
41
  TelemetryHelper.logActionEvent(LogLevel.WARN, {
29
42
  Event: TelemetryEvent.NetworkDisconnected
30
43
  });
31
- NotificationHandler.notifyError(NotificationScenarios.InternetConnection, (inMemoryState === null || inMemoryState === void 0 ? void 0 : (_inMemoryState$domain = inMemoryState.domainStates) === null || _inMemoryState$domain === void 0 ? void 0 : (_inMemoryState$domain2 = _inMemoryState$domain.middlewareLocalizedTexts) === null || _inMemoryState$domain2 === void 0 ? void 0 : _inMemoryState$domain2.MIDDLEWARE_BANNER_NO_INTERNET_CONNECTION) ?? defaultMiddlewareLocalizedTexts.MIDDLEWARE_BANNER_NO_INTERNET_CONNECTION);
44
+ NotificationHandler.notifyError(NotificationScenarios.InternetConnection, (inMemoryState === null || inMemoryState === void 0 ? void 0 : (_inMemoryState$domain3 = inMemoryState.domainStates) === null || _inMemoryState$domain3 === void 0 ? void 0 : (_inMemoryState$domain4 = _inMemoryState$domain3.middlewareLocalizedTexts) === null || _inMemoryState$domain4 === void 0 ? void 0 : _inMemoryState$domain4.MIDDLEWARE_BANNER_NO_INTERNET_CONNECTION) ?? defaultMiddlewareLocalizedTexts.MIDDLEWARE_BANNER_NO_INTERNET_CONNECTION);
32
45
  } else {
33
- var _inMemoryState$domain3, _inMemoryState$domain4;
46
+ var _inMemoryState$domain5, _inMemoryState$domain6;
34
47
  TelemetryHelper.logActionEvent(LogLevel.WARN, {
35
48
  Event: TelemetryEvent.NetworkReconnected
36
49
  });
37
- NotificationHandler.notifySuccess(NotificationScenarios.InternetConnection, (inMemoryState === null || inMemoryState === void 0 ? void 0 : (_inMemoryState$domain3 = inMemoryState.domainStates) === null || _inMemoryState$domain3 === void 0 ? void 0 : (_inMemoryState$domain4 = _inMemoryState$domain3.middlewareLocalizedTexts) === null || _inMemoryState$domain4 === void 0 ? void 0 : _inMemoryState$domain4.MIDDLEWARE_BANNER_INTERNET_BACK_ONLINE) ?? defaultMiddlewareLocalizedTexts.MIDDLEWARE_BANNER_INTERNET_BACK_ONLINE);
50
+ NotificationHandler.notifySuccess(NotificationScenarios.InternetConnection, (inMemoryState === null || inMemoryState === void 0 ? void 0 : (_inMemoryState$domain5 = inMemoryState.domainStates) === null || _inMemoryState$domain5 === void 0 ? void 0 : (_inMemoryState$domain6 = _inMemoryState$domain5.middlewareLocalizedTexts) === null || _inMemoryState$domain6 === void 0 ? void 0 : _inMemoryState$domain6.MIDDLEWARE_BANNER_INTERNET_BACK_ONLINE) ?? defaultMiddlewareLocalizedTexts.MIDDLEWARE_BANNER_INTERNET_BACK_ONLINE);
38
51
  BroadcastService.postMessage({
39
52
  eventName: BroadcastEvent.NetworkReconnected
40
53
  });
@@ -28,12 +28,65 @@ export const createMarkdown = (disableMarkdownMessageFormatting, disableNewLineM
28
28
  // Rule to process html blocks and paragraphs
29
29
  "html_inline",
30
30
  // Rule to process html tags
31
- "newline" // Rule to proceess '\n'
31
+ "newline",
32
+ // Rule to proceess '\n'
33
+ "list" // Enable list parsing rule
32
34
  ]);
33
35
  }
34
36
 
35
37
  markdown.disable(["strikethrough"]);
36
38
 
39
+ // Custom plugin to fix numbered list continuity
40
+ markdown.use(function (md) {
41
+ const originalRender = md.render.bind(md);
42
+ const originalRenderInline = md.renderInline.bind(md);
43
+ function preprocessText(text) {
44
+ // Handle numbered lists that come with double line breaks (knowledge article format)
45
+ // This ensures proper continuous numbering instead of separate lists
46
+
47
+ let result = text;
48
+
49
+ // Only process if the text contains the double line break pattern
50
+ // But exclude simple numbered lists (where content after \n\n starts with another number)
51
+ if (!/\d+\.\s+.*?\n\n(?!\d+\.\s)[\s\S]*?(?:\n\n\d+\.|\s*$)/.test(text)) {
52
+ return result;
53
+ }
54
+
55
+ // Convert "1. Item\n\nContent\n\n2. Item" to proper markdown list format
56
+ // Use improved pattern with negative lookahead to exclude cases where content starts with numbered list
57
+ const listPattern = /(\d+\.\s+[^\n]+)(\n\n(?!\d+\.\s)[\s\S]*?)(?=\n\n\d+\.|\s*$)/g;
58
+ if (listPattern.test(result)) {
59
+ // Reset regex state for actual replacement
60
+ listPattern.lastIndex = 0;
61
+ result = result.replace(listPattern, (match, listItem, content) => {
62
+ if (!content) {
63
+ return match;
64
+ }
65
+
66
+ // Format content with proper indentation
67
+ const cleanContent = content.substring(2); // Remove leading \n\n
68
+ const lines = cleanContent.split("\n");
69
+ const indentedContent = lines.map(line => line.trim() ? `${Constants.MARKDOWN_LIST_INDENTATION}${line}` : "").join("\n");
70
+ const lineBreak = disableNewLineMarkdownSupport ? "\n" : "\n\n";
71
+ return `${listItem}${lineBreak}${indentedContent}`;
72
+ });
73
+ }
74
+ return result;
75
+ }
76
+
77
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
78
+ md.render = function (text, env) {
79
+ const processedText = preprocessText(text);
80
+ return originalRender(processedText, env);
81
+ };
82
+
83
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
84
+ md.renderInline = function (text, env) {
85
+ const processedText = preprocessText(text);
86
+ return originalRenderInline(processedText, env);
87
+ };
88
+ });
89
+
37
90
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
38
91
  markdown.use(MarkdownItForInline, "url_new_win", "link_open", function (tokens, idx, env) {
39
92
  const targetAttrIndex = tokens[idx].attrIndex(Constants.Target);
@@ -0,0 +1,45 @@
1
+ import { Constants } from "../../../common/Constants";
2
+ import { TelemetryHelper } from "../../../common/telemetry/TelemetryHelper";
3
+ import { LogLevel, TelemetryEvent } from "../../../common/telemetry/TelemetryConstants";
4
+ import { getCustomEventValue, isValidCustomEvent } from "../../../common/utils";
5
+ export const customEventCallback = facadeChatSDK => event => {
6
+ if (!(Constants.payload in event)) return;
7
+ if (isValidCustomEvent(event.payload)) {
8
+ const customEventPayload = event.payload;
9
+ try {
10
+ const customEventValueStr = getCustomEventValue(customEventPayload);
11
+ const customEventName = customEventPayload.customEventName;
12
+ const messageMeta = {
13
+ customEvent: Constants.true,
14
+ customEventName: customEventName,
15
+ customEventValue: customEventValueStr
16
+ };
17
+ const messagePayload = {
18
+ content: "",
19
+ tags: [Constants.Hidden],
20
+ metadata: messageMeta,
21
+ timestamp: new Date()
22
+ };
23
+ facadeChatSDK.sendMessage(messagePayload);
24
+ TelemetryHelper.logActionEventToAllTelemetry(LogLevel.DEBUG, {
25
+ Event: TelemetryEvent.CustomEventAction,
26
+ Description: "Sent customEvent.",
27
+ CustomProperties: {
28
+ customEventName,
29
+ lengthCustomEventValue: customEventValueStr.length
30
+ }
31
+ });
32
+ } catch (error) {
33
+ TelemetryHelper.logActionEventToAllTelemetry(LogLevel.ERROR, {
34
+ Event: TelemetryEvent.CustomEventAction,
35
+ Description: "Failed to process CustomEvent.",
36
+ ExceptionDetails: {
37
+ error
38
+ }
39
+ });
40
+ }
41
+ }
42
+ };
43
+ export const subscribeToSendCustomEvent = (broadcastService, facadeChatSDK, customEventCallback) => {
44
+ broadcastService.getMessageByEventName(Constants.sendCustomEvent).subscribe(customEventCallback(facadeChatSDK));
45
+ };
@@ -35,6 +35,7 @@ const prepareEndChat = async (props, facadeChatSDK, state, dispatch, setAdapter,
35
35
  Description: PrepareEndChatDescriptionConstants.ConversationEndedByCustomerWithoutPostChat
36
36
  });
37
37
  await endChat(props, facadeChatSDK, state, dispatch, setAdapter, setWebChatStyles, adapter, false, false, true);
38
+ return;
38
39
  }
39
40
 
40
41
  // Use Case: If ended by Agent, stay chat in InActive state
@@ -210,7 +211,7 @@ const endChat = async (props, facadeChatSDK, state, dispatch, setAdapter, setWeb
210
211
  payload: undefined
211
212
  });
212
213
  // Always allow to close the chat for embedded mode irrespective of end chat errors
213
- closeChatWidget(dispatch);
214
+ closeChatWidget(dispatch, setWebChatStyles, props);
214
215
  facadeChatSDK.destroy();
215
216
  }
216
217
  }
@@ -338,12 +339,22 @@ export const endVoiceVideoCallIfOngoing = async (facadeChatSDK, dispatch) => {
338
339
  }, callId);
339
340
  }
340
341
  };
341
- const closeChatWidget = dispatch => {
342
+
343
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
344
+ const closeChatWidget = (dispatch, setWebChatStyles, props) => {
345
+ var _props$webChatContain2, _props$webChatContain3;
342
346
  // Embedded chat
343
347
  dispatch({
344
348
  type: LiveChatWidgetActionType.SET_CONVERSATION_STATE,
345
349
  payload: ConversationState.Closed
346
350
  });
351
+
352
+ // if customer is setting the hideSendbox, we should not alter its value
353
+ if ((props === null || props === void 0 ? void 0 : (_props$webChatContain2 = props.webChatContainerProps) === null || _props$webChatContain2 === void 0 ? void 0 : (_props$webChatContain3 = _props$webChatContain2.webChatStyles) === null || _props$webChatContain3 === void 0 ? void 0 : _props$webChatContain3.hideSendBox) === true) return;
354
+ setWebChatStyles(styles => ({
355
+ ...styles,
356
+ hideSendBox: false
357
+ }));
347
358
  };
348
359
 
349
360
  // eslint-disable-next-line @typescript-eslint/no-explicit-any