@microsoft/omnichannel-chat-widget 1.7.8-main.bd4de53 → 1.7.8-main.d71f599
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.
- package/lib/cjs/common/facades/FacadeChatSDK.js +15 -4
- package/lib/cjs/common/telemetry/TelemetryConstants.js +4 -0
- package/lib/cjs/components/livechatwidget/common/reconnectChatHelper.js +0 -5
- package/lib/cjs/components/livechatwidget/common/startChat.js +2 -23
- package/lib/cjs/firstresponselatency/FirstMessageTrackerFromBot.js +118 -0
- package/lib/cjs/firstresponselatency/FirstResponseLatencyTracker.js +32 -5
- package/lib/cjs/firstresponselatency/util.js +15 -2
- package/lib/esm/common/facades/FacadeChatSDK.js +15 -4
- package/lib/esm/common/telemetry/TelemetryConstants.js +4 -0
- package/lib/esm/components/livechatwidget/common/reconnectChatHelper.js +1 -6
- package/lib/esm/components/livechatwidget/common/startChat.js +3 -24
- package/lib/esm/firstresponselatency/FirstMessageTrackerFromBot.js +112 -0
- package/lib/esm/firstresponselatency/FirstResponseLatencyTracker.js +33 -6
- package/lib/esm/firstresponselatency/util.js +12 -0
- package/lib/types/common/facades/FacadeChatSDK.d.ts +1 -0
- package/lib/types/common/telemetry/TelemetryConstants.d.ts +5 -1
- package/lib/types/firstresponselatency/FirstMessageTrackerFromBot.d.ts +1 -0
- package/lib/types/firstresponselatency/FirstResponseLatencyTracker.d.ts +5 -0
- package/lib/types/firstresponselatency/util.d.ts +2 -1
- package/package.json +13 -3
|
@@ -148,9 +148,20 @@ class FacadeChatSDK {
|
|
|
148
148
|
}
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
|
+
async corroborateTokenIsSet(chatSDK) {
|
|
152
|
+
var _chatSDK$chatSDKConfi;
|
|
153
|
+
// if getAuthToken is not set, it's because handleAuthentication hasnt being called
|
|
154
|
+
// so we need to call it
|
|
155
|
+
if (this.isAuthenticated && (chatSDK === null || chatSDK === void 0 ? void 0 : (_chatSDK$chatSDKConfi = chatSDK.chatSDKConfig) === null || _chatSDK$chatSDKConfi === void 0 ? void 0 : _chatSDK$chatSDKConfi.getAuthToken) === undefined) {
|
|
156
|
+
(0, _authHelper.handleAuthentication)(this.chatSDK, this.chatConfig, this.getAuthToken);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
151
159
|
async tokenRing() {
|
|
152
160
|
var _this$chatSDK$chatSDK;
|
|
153
161
|
if (this.disableReauthentication === true) {
|
|
162
|
+
// Since we are not validating the token anymore, we at least need to check if the token is set
|
|
163
|
+
// no need to validate anything other that the token is set
|
|
164
|
+
await this.corroborateTokenIsSet(this.chatSDK);
|
|
154
165
|
// facade feature is disabled, so we are bypassing the re authentication and let it fail.
|
|
155
166
|
return {
|
|
156
167
|
result: true,
|
|
@@ -194,7 +205,7 @@ class FacadeChatSDK {
|
|
|
194
205
|
this.expiration = 0;
|
|
195
206
|
try {
|
|
196
207
|
const ring = await (0, _authHelper.handleAuthentication)(this.chatSDK, this.chatConfig, this.getAuthToken);
|
|
197
|
-
if (ring.result === true && ring.token) {
|
|
208
|
+
if ((ring === null || ring === void 0 ? void 0 : ring.result) === true && ring !== null && ring !== void 0 && ring.token) {
|
|
198
209
|
await this.setToken(ring.token);
|
|
199
210
|
_TelemetryHelper.TelemetryHelper.logFacadeChatSDKEvent(_TelemetryConstants.LogLevel.INFO, {
|
|
200
211
|
Event: _TelemetryConstants.TelemetryEvent.NewTokenSuccess,
|
|
@@ -211,12 +222,12 @@ class FacadeChatSDK {
|
|
|
211
222
|
var _ring$error, _ring$error2;
|
|
212
223
|
_TelemetryHelper.TelemetryHelper.logFacadeChatSDKEvent(_TelemetryConstants.LogLevel.ERROR, {
|
|
213
224
|
Event: _TelemetryConstants.TelemetryEvent.NewTokenFailed,
|
|
214
|
-
Description: (_ring$error = ring.error) === null || _ring$error === void 0 ? void 0 : _ring$error.message,
|
|
215
|
-
ExceptionDetails: ring.error
|
|
225
|
+
Description: ring === null || ring === void 0 ? void 0 : (_ring$error = ring.error) === null || _ring$error === void 0 ? void 0 : _ring$error.message,
|
|
226
|
+
ExceptionDetails: ring === null || ring === void 0 ? void 0 : ring.error
|
|
216
227
|
});
|
|
217
228
|
return {
|
|
218
229
|
result: false,
|
|
219
|
-
message: ((_ring$error2 = ring.error) === null || _ring$error2 === void 0 ? void 0 : _ring$error2.message) || "Failed to get token"
|
|
230
|
+
message: (ring === null || ring === void 0 ? void 0 : (_ring$error2 = ring.error) === null || _ring$error2 === void 0 ? void 0 : _ring$error2.message) || "Failed to get token"
|
|
220
231
|
};
|
|
221
232
|
}
|
|
222
233
|
} catch (e) {
|
|
@@ -66,6 +66,8 @@ exports.BroadcastEvent = BroadcastEvent;
|
|
|
66
66
|
BroadcastEvent["ContactIdNotFound"] = "ContactIdNotFound";
|
|
67
67
|
BroadcastEvent["SyncMinimize"] = "SyncMinimize";
|
|
68
68
|
BroadcastEvent["OnWidgetError"] = "OnWidgetError";
|
|
69
|
+
BroadcastEvent["FMLTrackingCompletedAck"] = "FMLTrackingCompletedAck";
|
|
70
|
+
BroadcastEvent["FMLTrackingCompleted"] = "FMLTrackingCompleted";
|
|
69
71
|
})(BroadcastEvent || (exports.BroadcastEvent = BroadcastEvent = {}));
|
|
70
72
|
let TelemetryEvent;
|
|
71
73
|
exports.TelemetryEvent = TelemetryEvent;
|
|
@@ -206,6 +208,8 @@ exports.TelemetryEvent = TelemetryEvent;
|
|
|
206
208
|
TelemetryEvent["MessageSent"] = "MessageSent";
|
|
207
209
|
TelemetryEvent["MessageReceived"] = "MessageReceived";
|
|
208
210
|
TelemetryEvent["MessageLapTrack"] = "MessageLapTrack";
|
|
211
|
+
TelemetryEvent["BotFirstMessageLapTrack"] = "BotFirstMessageLapTrack";
|
|
212
|
+
TelemetryEvent["BotFirstMessageLapTrackError"] = "BotFirstMessageLapTrackError";
|
|
209
213
|
TelemetryEvent["MessageStartLapTrackError"] = "MessageStartLapTrackError";
|
|
210
214
|
TelemetryEvent["MessageStopLapTrackError"] = "MessageStopLapTrackError";
|
|
211
215
|
TelemetryEvent["SystemMessageReceived"] = "SystemMessageReceived";
|
|
@@ -67,11 +67,6 @@ const getChatReconnectContext = async (facadeChatSDK, chatConfig, props, isAuthe
|
|
|
67
67
|
const chatReconnectOptionalParams = {
|
|
68
68
|
reconnectId: (_props$reconnectChatP4 = props.reconnectChatPaneProps) === null || _props$reconnectChatP4 === void 0 ? void 0 : _props$reconnectChatP4.reconnectId
|
|
69
69
|
};
|
|
70
|
-
// Get auth token for getting chat reconnect context
|
|
71
|
-
if (isAuthenticatedChat) {
|
|
72
|
-
// handle authentication will throw error if auth token is not available, so no need to check for response
|
|
73
|
-
await (0, _authHelper.handleAuthentication)(facadeChatSDK.getChatSDK(), chatConfig, props.getAuthToken);
|
|
74
|
-
}
|
|
75
70
|
const reconnectChatContext = await (facadeChatSDK === null || facadeChatSDK === void 0 ? void 0 : facadeChatSDK.getChatReconnectContext(chatReconnectOptionalParams));
|
|
76
71
|
if (isAuthenticatedChat) {
|
|
77
72
|
// remove auth token after reconnectId is fetched
|
|
@@ -7,7 +7,6 @@ exports.setPreChatAndInitiateChat = exports.prepareStartChat = exports.initStart
|
|
|
7
7
|
var _TelemetryConstants = require("../../../common/telemetry/TelemetryConstants");
|
|
8
8
|
var _Constants = require("../../../common/Constants");
|
|
9
9
|
var _utils = require("../../../common/utils");
|
|
10
|
-
var _authHelper = require("./authHelper");
|
|
11
10
|
var _reconnectChatHelper = require("./reconnectChatHelper");
|
|
12
11
|
var _startChatErrorHandler = require("./startChatErrorHandler");
|
|
13
12
|
var _ActivityStreamHandler = require("./ActivityStreamHandler");
|
|
@@ -19,6 +18,7 @@ var _TelemetryManager = require("../../../common/telemetry/TelemetryManager");
|
|
|
19
18
|
var _endChat = require("./endChat");
|
|
20
19
|
var _createAdapter = require("./createAdapter");
|
|
21
20
|
var _newMessageEventHandler = require("../../../plugins/newMessageEventHandler");
|
|
21
|
+
var _FirstMessageTrackerFromBot = require("../../../firstresponselatency/FirstMessageTrackerFromBot");
|
|
22
22
|
var _liveChatConfigUtils = require("./liveChatConfigUtils");
|
|
23
23
|
var _setPostChatContextAndLoadSurvey = require("./setPostChatContextAndLoadSurvey");
|
|
24
24
|
var _persistentChatHelper = require("./persistentChatHelper");
|
|
@@ -30,20 +30,6 @@ let widgetInstanceId;
|
|
|
30
30
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
31
31
|
let popoutWidgetInstanceId;
|
|
32
32
|
|
|
33
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
34
|
-
const setAuthenticationIfApplicable = async (props, facadeChatSDK) => {
|
|
35
|
-
const chatConfig = props === null || props === void 0 ? void 0 : props.chatConfig;
|
|
36
|
-
const getAuthToken = props === null || props === void 0 ? void 0 : props.getAuthToken;
|
|
37
|
-
const authClientFunction = (0, _authHelper.getAuthClientFunction)(chatConfig);
|
|
38
|
-
if (getAuthToken && authClientFunction) {
|
|
39
|
-
// set auth token to chat sdk before start chat
|
|
40
|
-
const authSuccess = await (0, _authHelper.handleAuthentication)(facadeChatSDK.getChatSDK(), chatConfig, getAuthToken);
|
|
41
|
-
if (!authSuccess.result) {
|
|
42
|
-
throw new Error(_Constants.WidgetLoadCustomErrorString.AuthenticationFailedErrorString);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
|
|
47
33
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
48
34
|
const prepareStartChat = async (props, facadeChatSDK, state, dispatch, setAdapter) => {
|
|
49
35
|
optionalParams = {}; //Resetting to ensure no stale values
|
|
@@ -71,11 +57,6 @@ const prepareStartChat = async (props, facadeChatSDK, state, dispatch, setAdapte
|
|
|
71
57
|
const isProactiveChat = state.appStates.conversationState === _ConversationState.ConversationState.ProactiveChat;
|
|
72
58
|
const isPreChatEnabledInProactiveChat = state.appStates.proactiveChatStates.proactiveChatEnablePrechat;
|
|
73
59
|
|
|
74
|
-
// Setting auth settings to OC API to retrieve existing persistent chat session before start chat if any
|
|
75
|
-
if ((0, _reconnectChatHelper.isPersistentEnabled)(props.chatConfig)) {
|
|
76
|
-
await setAuthenticationIfApplicable(props, facadeChatSDK);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
60
|
//Setting PreChat and intiate chat
|
|
80
61
|
await setPreChatAndInitiateChat(facadeChatSDK, dispatch, setAdapter, isProactiveChat, isPreChatEnabledInProactiveChat, state, props);
|
|
81
62
|
};
|
|
@@ -151,6 +132,7 @@ const setPreChatAndInitiateChat = async (facadeChatSDK, dispatch, setAdapter, is
|
|
|
151
132
|
const optionalParams = {
|
|
152
133
|
isProactiveChat
|
|
153
134
|
};
|
|
135
|
+
(0, _FirstMessageTrackerFromBot.createTrackingForFirstMessage)();
|
|
154
136
|
await initStartChat(facadeChatSDK, dispatch, setAdapter, state, props, optionalParams);
|
|
155
137
|
};
|
|
156
138
|
|
|
@@ -179,9 +161,6 @@ const initStartChat = async (facadeChatSDK, dispatch, setAdapter, state, props,
|
|
|
179
161
|
Description: "Widget loading started"
|
|
180
162
|
});
|
|
181
163
|
|
|
182
|
-
// Auth token retrieval needs to happen during start chat to support pop-out chat
|
|
183
|
-
await setAuthenticationIfApplicable(props, facadeChatSDK);
|
|
184
|
-
|
|
185
164
|
//Check if chat retrieved from cache
|
|
186
165
|
if (persistedState || params !== null && params !== void 0 && params.liveChatContext) {
|
|
187
166
|
var _persistedState$domai, _persistedState$domai2, _persistedState$domai3, _persistedState$domai4, _persistedState$domai5;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.createTrackingForFirstMessage = void 0;
|
|
7
|
+
var _TelemetryConstants = require("../common/telemetry/TelemetryConstants");
|
|
8
|
+
var _omnichannelChatComponents = require("@microsoft/omnichannel-chat-components");
|
|
9
|
+
var _TelemetryHelper = require("../common/telemetry/TelemetryHelper");
|
|
10
|
+
var _util = require("./util");
|
|
11
|
+
// This tracker is event based, this is since we are tracking events coming from different sources
|
|
12
|
+
// with different timeline, therefore this is a functional approach to track the events, instead of a class based approach
|
|
13
|
+
const createTrackingForFirstMessage = () => {
|
|
14
|
+
// Reset the tracking variables
|
|
15
|
+
let startTracking = false;
|
|
16
|
+
let stopTracking = false;
|
|
17
|
+
let startTime = 0;
|
|
18
|
+
let stopTime = 0;
|
|
19
|
+
let stopTrackingMessage;
|
|
20
|
+
let flag = false;
|
|
21
|
+
const isMessageFromValidSender = payload => {
|
|
22
|
+
var _payload$tags;
|
|
23
|
+
// agent scenario
|
|
24
|
+
if (payload !== null && payload !== void 0 && (_payload$tags = payload.tags) !== null && _payload$tags !== void 0 && _payload$tags.includes("public")) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
return true;
|
|
28
|
+
};
|
|
29
|
+
const widgetLoadListener = _omnichannelChatComponents.BroadcastService.getMessageByEventName(_TelemetryConstants.TelemetryEvent.WidgetLoadComplete).subscribe(() => {
|
|
30
|
+
if (startTracking) return;
|
|
31
|
+
startTracking = true;
|
|
32
|
+
startTime = new Date().getTime();
|
|
33
|
+
});
|
|
34
|
+
const newMessageListener = _omnichannelChatComponents.BroadcastService.getMessageByEventName(_TelemetryConstants.BroadcastEvent.NewMessageReceived).subscribe(message => {
|
|
35
|
+
const payload = message.payload;
|
|
36
|
+
|
|
37
|
+
// we only care for bot, so we need to check if the message is from the bot
|
|
38
|
+
// pending to add typing message indicator signal detection
|
|
39
|
+
|
|
40
|
+
if (isMessageFromValidSender(payload)) {
|
|
41
|
+
if (startTracking && !stopTracking) {
|
|
42
|
+
stopTime = new Date().getTime();
|
|
43
|
+
const elapsedTime = stopTime - startTime;
|
|
44
|
+
stopTracking = true;
|
|
45
|
+
stopTrackingMessage = (0, _util.createTrackingMessage)(payload, "botMessage");
|
|
46
|
+
notifyFMLTrackingCompleted();
|
|
47
|
+
_TelemetryHelper.TelemetryHelper.logActionEvent(_TelemetryConstants.LogLevel.INFO, {
|
|
48
|
+
Event: _TelemetryConstants.TelemetryEvent.BotFirstMessageLapTrack,
|
|
49
|
+
Description: "First Message from Bot latency tracking",
|
|
50
|
+
CustomProperties: {
|
|
51
|
+
elapsedTime,
|
|
52
|
+
widgetLoadedAt: startTime,
|
|
53
|
+
botMessage: stopTrackingMessage
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// this track only first message, if coming from the bot or not
|
|
60
|
+
// the only difference is that it logs only those from bot
|
|
61
|
+
disconnectListener();
|
|
62
|
+
});
|
|
63
|
+
const notifyFMLTrackingCompleted = () => {
|
|
64
|
+
ackListener();
|
|
65
|
+
// Retry sending until flag is true, but do not block the main thread
|
|
66
|
+
const interval = setInterval(() => {
|
|
67
|
+
if (flag) {
|
|
68
|
+
clearInterval(interval);
|
|
69
|
+
} else {
|
|
70
|
+
_omnichannelChatComponents.BroadcastService.postMessage({
|
|
71
|
+
eventName: _TelemetryConstants.BroadcastEvent.FMLTrackingCompleted,
|
|
72
|
+
payload: null
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}, 100);
|
|
76
|
+
};
|
|
77
|
+
const ackListener = () => {
|
|
78
|
+
const listen = _omnichannelChatComponents.BroadcastService.getMessageByEventName(_TelemetryConstants.BroadcastEvent.FMLTrackingCompletedAck).subscribe(() => {
|
|
79
|
+
flag = true;
|
|
80
|
+
listen.unsubscribe();
|
|
81
|
+
});
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
// Rehydrate message is received when the widget is reloaded, this is to ensure that we are not tracking messages that are not part of the current conversation
|
|
85
|
+
// No need to keep listerning for tracking, enforcing disconnection for the listners
|
|
86
|
+
const rehydrateListener = _omnichannelChatComponents.BroadcastService.getMessageByEventName(_TelemetryConstants.TelemetryEvent.RehydrateMessageReceived).subscribe(() => {
|
|
87
|
+
startTracking = false;
|
|
88
|
+
stopTracking = false;
|
|
89
|
+
disconnectListener();
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// Rehydrate message is received when the widget is reloaded, this is to ensure that we are not tracking messages that are not part of the current conversation
|
|
93
|
+
// No need to keep listerning for tracking, enforcing disconnection for the listners
|
|
94
|
+
const historyListener = _omnichannelChatComponents.BroadcastService.getMessageByEventName(_TelemetryConstants.BroadcastEvent.HistoryMessageReceived).subscribe(() => {
|
|
95
|
+
startTracking = false;
|
|
96
|
+
stopTracking = false;
|
|
97
|
+
disconnectListener();
|
|
98
|
+
});
|
|
99
|
+
const offlineNetworkListener = _omnichannelChatComponents.BroadcastService.getMessageByEventName(_TelemetryConstants.TelemetryEvent.NetworkDisconnected).subscribe(() => {
|
|
100
|
+
startTracking = false;
|
|
101
|
+
stopTracking = false;
|
|
102
|
+
disconnectListener();
|
|
103
|
+
_TelemetryHelper.TelemetryHelper.logActionEvent(_TelemetryConstants.LogLevel.INFO, {
|
|
104
|
+
Event: _TelemetryConstants.TelemetryEvent.BotFirstMessageLapTrackError,
|
|
105
|
+
Description: "Tracker Stopped due to network disconnection"
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// this is to ensure that we are not tracking messages that are not part of the current conversation
|
|
110
|
+
const disconnectListener = () => {
|
|
111
|
+
historyListener.unsubscribe();
|
|
112
|
+
rehydrateListener.unsubscribe();
|
|
113
|
+
newMessageListener.unsubscribe();
|
|
114
|
+
widgetLoadListener.unsubscribe();
|
|
115
|
+
offlineNetworkListener.unsubscribe();
|
|
116
|
+
};
|
|
117
|
+
};
|
|
118
|
+
exports.createTrackingForFirstMessage = createTrackingForFirstMessage;
|
|
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.FirstResponseLatencyTracker = void 0;
|
|
7
7
|
var _TelemetryConstants = require("../common/telemetry/TelemetryConstants");
|
|
8
|
+
var _omnichannelChatComponents = require("@microsoft/omnichannel-chat-components");
|
|
8
9
|
var _TelemetryHelper = require("../common/telemetry/TelemetryHelper");
|
|
9
10
|
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
10
11
|
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
|
|
@@ -16,6 +17,32 @@ class FirstResponseLatencyTracker {
|
|
|
16
17
|
_defineProperty(this, "isEnded", false);
|
|
17
18
|
_defineProperty(this, "startTrackingMessage", void 0);
|
|
18
19
|
_defineProperty(this, "stopTrackingMessage", void 0);
|
|
20
|
+
_defineProperty(this, "isReady", false);
|
|
21
|
+
_defineProperty(this, "offlineNetworkListener", _omnichannelChatComponents.BroadcastService.getMessageByEventName(_TelemetryConstants.TelemetryEvent.NetworkDisconnected).subscribe(() => {
|
|
22
|
+
this.isStarted = false;
|
|
23
|
+
this.isEnded = false;
|
|
24
|
+
_TelemetryHelper.TelemetryHelper.logActionEvent(_TelemetryConstants.LogLevel.INFO, {
|
|
25
|
+
Event: _TelemetryConstants.TelemetryEvent.MessageStopLapTrackError,
|
|
26
|
+
Description: "Tracker Stopped due to network disconnection"
|
|
27
|
+
});
|
|
28
|
+
}));
|
|
29
|
+
_defineProperty(this, "fmltrackingListener", _omnichannelChatComponents.BroadcastService.getMessageByEventName(_TelemetryConstants.BroadcastEvent.FMLTrackingCompleted).subscribe(() => {
|
|
30
|
+
this.isReady = true;
|
|
31
|
+
_omnichannelChatComponents.BroadcastService.postMessage({
|
|
32
|
+
eventName: _TelemetryConstants.BroadcastEvent.FMLTrackingCompletedAck,
|
|
33
|
+
payload: null
|
|
34
|
+
});
|
|
35
|
+
}));
|
|
36
|
+
// Rehydrate message is received when the widget is reloaded, this is to ensure that we are not tracking messages that are not part of the current conversation
|
|
37
|
+
// No need to keep listerning for tracking, enforcing disconnection for the listners
|
|
38
|
+
_defineProperty(this, "rehydrateListener", _omnichannelChatComponents.BroadcastService.getMessageByEventName(_TelemetryConstants.TelemetryEvent.RehydrateMessageReceived).subscribe(() => {
|
|
39
|
+
this.isReady = true;
|
|
40
|
+
}));
|
|
41
|
+
// Rehydrate message is received when the widget is reloaded, this is to ensure that we are not tracking messages that are not part of the current conversation
|
|
42
|
+
// No need to keep listerning for tracking, enforcing disconnection for the listners
|
|
43
|
+
_defineProperty(this, "historyListener", _omnichannelChatComponents.BroadcastService.getMessageByEventName(_TelemetryConstants.BroadcastEvent.HistoryMessageReceived).subscribe(() => {
|
|
44
|
+
this.isReady = true;
|
|
45
|
+
}));
|
|
19
46
|
// this is a workaround to ensure in reload we track effectively the messages
|
|
20
47
|
// we do have a mechanism in place to prevent log agent messages.
|
|
21
48
|
this.isABotConversation = true;
|
|
@@ -35,6 +62,7 @@ class FirstResponseLatencyTracker {
|
|
|
35
62
|
|
|
36
63
|
// Tracking Functions
|
|
37
64
|
startTracking(payload) {
|
|
65
|
+
if (!this.isReady) return;
|
|
38
66
|
// this prevents to initiate tracking for multiple incoming messages
|
|
39
67
|
if (this.isStarted) {
|
|
40
68
|
return;
|
|
@@ -43,11 +71,9 @@ class FirstResponseLatencyTracker {
|
|
|
43
71
|
if (!this.isABotConversation) {
|
|
44
72
|
return;
|
|
45
73
|
}
|
|
46
|
-
|
|
47
74
|
// control of states to prevent clashing of messages
|
|
48
75
|
this.isStarted = true;
|
|
49
76
|
this.isEnded = false;
|
|
50
|
-
|
|
51
77
|
// The idea of using types is to enrich telemetry data
|
|
52
78
|
this.startTrackingMessage = this.createTrackingMessage(payload, "userMessage");
|
|
53
79
|
}
|
|
@@ -99,9 +125,6 @@ class FirstResponseLatencyTracker {
|
|
|
99
125
|
if (!payload || !payload.Id) {
|
|
100
126
|
throw new Error("Invalid payload");
|
|
101
127
|
}
|
|
102
|
-
// in the case of a reload, tracker will be paused, until last history message is received
|
|
103
|
-
// this is because we dont have a way to identidy send messages as part of the history
|
|
104
|
-
//if (this.inPause) return;
|
|
105
128
|
this.startTracking(payload);
|
|
106
129
|
} catch (e) {
|
|
107
130
|
_TelemetryHelper.TelemetryHelper.logActionEvent(_TelemetryConstants.LogLevel.ERROR, {
|
|
@@ -147,6 +170,10 @@ class FirstResponseLatencyTracker {
|
|
|
147
170
|
this.isEnded = false;
|
|
148
171
|
this.startTrackingMessage = undefined;
|
|
149
172
|
this.stopTrackingMessage = undefined;
|
|
173
|
+
this.offlineNetworkListener.unsubscribe();
|
|
174
|
+
this.fmltrackingListener.unsubscribe();
|
|
175
|
+
this.rehydrateListener.unsubscribe();
|
|
176
|
+
this.historyListener.unsubscribe();
|
|
150
177
|
}
|
|
151
178
|
}
|
|
152
179
|
exports.FirstResponseLatencyTracker = FirstResponseLatencyTracker;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.polyfillMessagePayloadForEvent = exports.isHistoryMessage = exports.getScenarioType = exports.buildMessagePayload = void 0;
|
|
6
|
+
exports.polyfillMessagePayloadForEvent = exports.isHistoryMessage = exports.getScenarioType = exports.createTrackingMessage = exports.buildMessagePayload = void 0;
|
|
7
7
|
var _Constants = require("./Constants");
|
|
8
8
|
var _Constants2 = require("../common/Constants");
|
|
9
9
|
const isHistoryMessage = (activity, startTime) => {
|
|
@@ -82,4 +82,17 @@ const getScenarioType = activity => {
|
|
|
82
82
|
}
|
|
83
83
|
return _Constants.ScenarioType.ReceivedMessageStrategy;
|
|
84
84
|
};
|
|
85
|
-
exports.getScenarioType = getScenarioType;
|
|
85
|
+
exports.getScenarioType = getScenarioType;
|
|
86
|
+
const createTrackingMessage = (payload, type) => {
|
|
87
|
+
return {
|
|
88
|
+
Id: payload.Id,
|
|
89
|
+
role: payload.role,
|
|
90
|
+
timestamp: payload === null || payload === void 0 ? void 0 : payload.timestamp,
|
|
91
|
+
tags: payload.tags,
|
|
92
|
+
messageType: payload.messageType,
|
|
93
|
+
text: payload.text,
|
|
94
|
+
type: type,
|
|
95
|
+
checkTime: new Date().getTime()
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
exports.createTrackingMessage = createTrackingMessage;
|
|
@@ -142,9 +142,20 @@ export class FacadeChatSDK {
|
|
|
142
142
|
}
|
|
143
143
|
}
|
|
144
144
|
}
|
|
145
|
+
async corroborateTokenIsSet(chatSDK) {
|
|
146
|
+
var _chatSDK$chatSDKConfi;
|
|
147
|
+
// if getAuthToken is not set, it's because handleAuthentication hasnt being called
|
|
148
|
+
// so we need to call it
|
|
149
|
+
if (this.isAuthenticated && (chatSDK === null || chatSDK === void 0 ? void 0 : (_chatSDK$chatSDKConfi = chatSDK.chatSDKConfig) === null || _chatSDK$chatSDKConfi === void 0 ? void 0 : _chatSDK$chatSDKConfi.getAuthToken) === undefined) {
|
|
150
|
+
handleAuthentication(this.chatSDK, this.chatConfig, this.getAuthToken);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
145
153
|
async tokenRing() {
|
|
146
154
|
var _this$chatSDK$chatSDK;
|
|
147
155
|
if (this.disableReauthentication === true) {
|
|
156
|
+
// Since we are not validating the token anymore, we at least need to check if the token is set
|
|
157
|
+
// no need to validate anything other that the token is set
|
|
158
|
+
await this.corroborateTokenIsSet(this.chatSDK);
|
|
148
159
|
// facade feature is disabled, so we are bypassing the re authentication and let it fail.
|
|
149
160
|
return {
|
|
150
161
|
result: true,
|
|
@@ -188,7 +199,7 @@ export class FacadeChatSDK {
|
|
|
188
199
|
this.expiration = 0;
|
|
189
200
|
try {
|
|
190
201
|
const ring = await handleAuthentication(this.chatSDK, this.chatConfig, this.getAuthToken);
|
|
191
|
-
if (ring.result === true && ring.token) {
|
|
202
|
+
if ((ring === null || ring === void 0 ? void 0 : ring.result) === true && ring !== null && ring !== void 0 && ring.token) {
|
|
192
203
|
await this.setToken(ring.token);
|
|
193
204
|
TelemetryHelper.logFacadeChatSDKEvent(LogLevel.INFO, {
|
|
194
205
|
Event: TelemetryEvent.NewTokenSuccess,
|
|
@@ -205,12 +216,12 @@ export class FacadeChatSDK {
|
|
|
205
216
|
var _ring$error, _ring$error2;
|
|
206
217
|
TelemetryHelper.logFacadeChatSDKEvent(LogLevel.ERROR, {
|
|
207
218
|
Event: TelemetryEvent.NewTokenFailed,
|
|
208
|
-
Description: (_ring$error = ring.error) === null || _ring$error === void 0 ? void 0 : _ring$error.message,
|
|
209
|
-
ExceptionDetails: ring.error
|
|
219
|
+
Description: ring === null || ring === void 0 ? void 0 : (_ring$error = ring.error) === null || _ring$error === void 0 ? void 0 : _ring$error.message,
|
|
220
|
+
ExceptionDetails: ring === null || ring === void 0 ? void 0 : ring.error
|
|
210
221
|
});
|
|
211
222
|
return {
|
|
212
223
|
result: false,
|
|
213
|
-
message: ((_ring$error2 = ring.error) === null || _ring$error2 === void 0 ? void 0 : _ring$error2.message) || "Failed to get token"
|
|
224
|
+
message: (ring === null || ring === void 0 ? void 0 : (_ring$error2 = ring.error) === null || _ring$error2 === void 0 ? void 0 : _ring$error2.message) || "Failed to get token"
|
|
214
225
|
};
|
|
215
226
|
}
|
|
216
227
|
} catch (e) {
|
|
@@ -61,6 +61,8 @@ export let BroadcastEvent;
|
|
|
61
61
|
BroadcastEvent["ContactIdNotFound"] = "ContactIdNotFound";
|
|
62
62
|
BroadcastEvent["SyncMinimize"] = "SyncMinimize";
|
|
63
63
|
BroadcastEvent["OnWidgetError"] = "OnWidgetError";
|
|
64
|
+
BroadcastEvent["FMLTrackingCompletedAck"] = "FMLTrackingCompletedAck";
|
|
65
|
+
BroadcastEvent["FMLTrackingCompleted"] = "FMLTrackingCompleted";
|
|
64
66
|
})(BroadcastEvent || (BroadcastEvent = {}));
|
|
65
67
|
export let TelemetryEvent;
|
|
66
68
|
(function (TelemetryEvent) {
|
|
@@ -200,6 +202,8 @@ export let TelemetryEvent;
|
|
|
200
202
|
TelemetryEvent["MessageSent"] = "MessageSent";
|
|
201
203
|
TelemetryEvent["MessageReceived"] = "MessageReceived";
|
|
202
204
|
TelemetryEvent["MessageLapTrack"] = "MessageLapTrack";
|
|
205
|
+
TelemetryEvent["BotFirstMessageLapTrack"] = "BotFirstMessageLapTrack";
|
|
206
|
+
TelemetryEvent["BotFirstMessageLapTrackError"] = "BotFirstMessageLapTrackError";
|
|
203
207
|
TelemetryEvent["MessageStartLapTrackError"] = "MessageStartLapTrackError";
|
|
204
208
|
TelemetryEvent["MessageStopLapTrackError"] = "MessageStopLapTrackError";
|
|
205
209
|
TelemetryEvent["SystemMessageReceived"] = "SystemMessageReceived";
|
|
@@ -2,7 +2,7 @@ import "regenerator-runtime/runtime";
|
|
|
2
2
|
import { BroadcastEvent, LogLevel, TelemetryEvent } from "../../../common/telemetry/TelemetryConstants";
|
|
3
3
|
import { ConversationMode, WidgetLoadCustomErrorString } from "../../../common/Constants";
|
|
4
4
|
import { checkContactIdError, isNullOrEmptyString, isNullOrUndefined } from "../../../common/utils";
|
|
5
|
-
import {
|
|
5
|
+
import { removeAuthTokenProvider } from "./authHelper";
|
|
6
6
|
import { BroadcastService } from "@microsoft/omnichannel-chat-components";
|
|
7
7
|
import { ConversationState } from "../../../contexts/common/ConversationState";
|
|
8
8
|
import { LiveChatWidgetActionType } from "../../../contexts/common/LiveChatWidgetActionType";
|
|
@@ -61,11 +61,6 @@ const getChatReconnectContext = async (facadeChatSDK, chatConfig, props, isAuthe
|
|
|
61
61
|
const chatReconnectOptionalParams = {
|
|
62
62
|
reconnectId: (_props$reconnectChatP4 = props.reconnectChatPaneProps) === null || _props$reconnectChatP4 === void 0 ? void 0 : _props$reconnectChatP4.reconnectId
|
|
63
63
|
};
|
|
64
|
-
// Get auth token for getting chat reconnect context
|
|
65
|
-
if (isAuthenticatedChat) {
|
|
66
|
-
// handle authentication will throw error if auth token is not available, so no need to check for response
|
|
67
|
-
await handleAuthentication(facadeChatSDK.getChatSDK(), chatConfig, props.getAuthToken);
|
|
68
|
-
}
|
|
69
64
|
const reconnectChatContext = await (facadeChatSDK === null || facadeChatSDK === void 0 ? void 0 : facadeChatSDK.getChatReconnectContext(chatReconnectOptionalParams));
|
|
70
65
|
if (isAuthenticatedChat) {
|
|
71
66
|
// remove auth token after reconnectId is fetched
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { BroadcastEvent, LogLevel, TelemetryEvent } from "../../../common/telemetry/TelemetryConstants";
|
|
2
|
-
import { Constants, LiveWorkItemState,
|
|
2
|
+
import { Constants, LiveWorkItemState, WidgetLoadTelemetryMessage } from "../../../common/Constants";
|
|
3
3
|
import { checkContactIdError, createTimer, getConversationDetailsCall, getStateFromCache, getWidgetCacheIdfromProps, isNullOrEmptyString, isNullOrUndefined, isUndefinedOrEmpty } from "../../../common/utils";
|
|
4
|
-
import { getAuthClientFunction, handleAuthentication } from "./authHelper";
|
|
5
4
|
import { handleChatReconnect, isPersistentEnabled, isReconnectEnabled } from "./reconnectChatHelper";
|
|
6
5
|
import { handleStartChatError, logWidgetLoadComplete } from "./startChatErrorHandler";
|
|
7
6
|
import { ActivityStreamHandler } from "./ActivityStreamHandler";
|
|
@@ -13,6 +12,7 @@ import { TelemetryTimers } from "../../../common/telemetry/TelemetryManager";
|
|
|
13
12
|
import { chatSDKStateCleanUp } from "./endChat";
|
|
14
13
|
import { createAdapter } from "./createAdapter";
|
|
15
14
|
import { createOnNewAdapterActivityHandler } from "../../../plugins/newMessageEventHandler";
|
|
15
|
+
import { createTrackingForFirstMessage } from "../../../firstresponselatency/FirstMessageTrackerFromBot";
|
|
16
16
|
import { isPersistentChatEnabled } from "./liveChatConfigUtils";
|
|
17
17
|
import { setPostChatContextAndLoadSurvey } from "./setPostChatContextAndLoadSurvey";
|
|
18
18
|
import { shouldSetPreChatIfPersistentChat } from "./persistentChatHelper";
|
|
@@ -25,20 +25,6 @@ let widgetInstanceId;
|
|
|
25
25
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
26
26
|
let popoutWidgetInstanceId;
|
|
27
27
|
|
|
28
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
29
|
-
const setAuthenticationIfApplicable = async (props, facadeChatSDK) => {
|
|
30
|
-
const chatConfig = props === null || props === void 0 ? void 0 : props.chatConfig;
|
|
31
|
-
const getAuthToken = props === null || props === void 0 ? void 0 : props.getAuthToken;
|
|
32
|
-
const authClientFunction = getAuthClientFunction(chatConfig);
|
|
33
|
-
if (getAuthToken && authClientFunction) {
|
|
34
|
-
// set auth token to chat sdk before start chat
|
|
35
|
-
const authSuccess = await handleAuthentication(facadeChatSDK.getChatSDK(), chatConfig, getAuthToken);
|
|
36
|
-
if (!authSuccess.result) {
|
|
37
|
-
throw new Error(WidgetLoadCustomErrorString.AuthenticationFailedErrorString);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
};
|
|
41
|
-
|
|
42
28
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
43
29
|
const prepareStartChat = async (props, facadeChatSDK, state, dispatch, setAdapter) => {
|
|
44
30
|
optionalParams = {}; //Resetting to ensure no stale values
|
|
@@ -66,11 +52,6 @@ const prepareStartChat = async (props, facadeChatSDK, state, dispatch, setAdapte
|
|
|
66
52
|
const isProactiveChat = state.appStates.conversationState === ConversationState.ProactiveChat;
|
|
67
53
|
const isPreChatEnabledInProactiveChat = state.appStates.proactiveChatStates.proactiveChatEnablePrechat;
|
|
68
54
|
|
|
69
|
-
// Setting auth settings to OC API to retrieve existing persistent chat session before start chat if any
|
|
70
|
-
if (isPersistentEnabled(props.chatConfig)) {
|
|
71
|
-
await setAuthenticationIfApplicable(props, facadeChatSDK);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
55
|
//Setting PreChat and intiate chat
|
|
75
56
|
await setPreChatAndInitiateChat(facadeChatSDK, dispatch, setAdapter, isProactiveChat, isPreChatEnabledInProactiveChat, state, props);
|
|
76
57
|
};
|
|
@@ -145,6 +126,7 @@ const setPreChatAndInitiateChat = async (facadeChatSDK, dispatch, setAdapter, is
|
|
|
145
126
|
const optionalParams = {
|
|
146
127
|
isProactiveChat
|
|
147
128
|
};
|
|
129
|
+
createTrackingForFirstMessage();
|
|
148
130
|
await initStartChat(facadeChatSDK, dispatch, setAdapter, state, props, optionalParams);
|
|
149
131
|
};
|
|
150
132
|
|
|
@@ -172,9 +154,6 @@ const initStartChat = async (facadeChatSDK, dispatch, setAdapter, state, props,
|
|
|
172
154
|
Description: "Widget loading started"
|
|
173
155
|
});
|
|
174
156
|
|
|
175
|
-
// Auth token retrieval needs to happen during start chat to support pop-out chat
|
|
176
|
-
await setAuthenticationIfApplicable(props, facadeChatSDK);
|
|
177
|
-
|
|
178
157
|
//Check if chat retrieved from cache
|
|
179
158
|
if (persistedState || params !== null && params !== void 0 && params.liveChatContext) {
|
|
180
159
|
var _persistedState$domai, _persistedState$domai2, _persistedState$domai3, _persistedState$domai4, _persistedState$domai5;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { BroadcastEvent, LogLevel, TelemetryEvent } from "../common/telemetry/TelemetryConstants";
|
|
2
|
+
import { BroadcastService } from "@microsoft/omnichannel-chat-components";
|
|
3
|
+
import { TelemetryHelper } from "../common/telemetry/TelemetryHelper";
|
|
4
|
+
import { createTrackingMessage } from "./util";
|
|
5
|
+
|
|
6
|
+
// This tracker is event based, this is since we are tracking events coming from different sources
|
|
7
|
+
// with different timeline, therefore this is a functional approach to track the events, instead of a class based approach
|
|
8
|
+
export const createTrackingForFirstMessage = () => {
|
|
9
|
+
// Reset the tracking variables
|
|
10
|
+
let startTracking = false;
|
|
11
|
+
let stopTracking = false;
|
|
12
|
+
let startTime = 0;
|
|
13
|
+
let stopTime = 0;
|
|
14
|
+
let stopTrackingMessage;
|
|
15
|
+
let flag = false;
|
|
16
|
+
const isMessageFromValidSender = payload => {
|
|
17
|
+
var _payload$tags;
|
|
18
|
+
// agent scenario
|
|
19
|
+
if (payload !== null && payload !== void 0 && (_payload$tags = payload.tags) !== null && _payload$tags !== void 0 && _payload$tags.includes("public")) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
return true;
|
|
23
|
+
};
|
|
24
|
+
const widgetLoadListener = BroadcastService.getMessageByEventName(TelemetryEvent.WidgetLoadComplete).subscribe(() => {
|
|
25
|
+
if (startTracking) return;
|
|
26
|
+
startTracking = true;
|
|
27
|
+
startTime = new Date().getTime();
|
|
28
|
+
});
|
|
29
|
+
const newMessageListener = BroadcastService.getMessageByEventName(BroadcastEvent.NewMessageReceived).subscribe(message => {
|
|
30
|
+
const payload = message.payload;
|
|
31
|
+
|
|
32
|
+
// we only care for bot, so we need to check if the message is from the bot
|
|
33
|
+
// pending to add typing message indicator signal detection
|
|
34
|
+
|
|
35
|
+
if (isMessageFromValidSender(payload)) {
|
|
36
|
+
if (startTracking && !stopTracking) {
|
|
37
|
+
stopTime = new Date().getTime();
|
|
38
|
+
const elapsedTime = stopTime - startTime;
|
|
39
|
+
stopTracking = true;
|
|
40
|
+
stopTrackingMessage = createTrackingMessage(payload, "botMessage");
|
|
41
|
+
notifyFMLTrackingCompleted();
|
|
42
|
+
TelemetryHelper.logActionEvent(LogLevel.INFO, {
|
|
43
|
+
Event: TelemetryEvent.BotFirstMessageLapTrack,
|
|
44
|
+
Description: "First Message from Bot latency tracking",
|
|
45
|
+
CustomProperties: {
|
|
46
|
+
elapsedTime,
|
|
47
|
+
widgetLoadedAt: startTime,
|
|
48
|
+
botMessage: stopTrackingMessage
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// this track only first message, if coming from the bot or not
|
|
55
|
+
// the only difference is that it logs only those from bot
|
|
56
|
+
disconnectListener();
|
|
57
|
+
});
|
|
58
|
+
const notifyFMLTrackingCompleted = () => {
|
|
59
|
+
ackListener();
|
|
60
|
+
// Retry sending until flag is true, but do not block the main thread
|
|
61
|
+
const interval = setInterval(() => {
|
|
62
|
+
if (flag) {
|
|
63
|
+
clearInterval(interval);
|
|
64
|
+
} else {
|
|
65
|
+
BroadcastService.postMessage({
|
|
66
|
+
eventName: BroadcastEvent.FMLTrackingCompleted,
|
|
67
|
+
payload: null
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}, 100);
|
|
71
|
+
};
|
|
72
|
+
const ackListener = () => {
|
|
73
|
+
const listen = BroadcastService.getMessageByEventName(BroadcastEvent.FMLTrackingCompletedAck).subscribe(() => {
|
|
74
|
+
flag = true;
|
|
75
|
+
listen.unsubscribe();
|
|
76
|
+
});
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// Rehydrate message is received when the widget is reloaded, this is to ensure that we are not tracking messages that are not part of the current conversation
|
|
80
|
+
// No need to keep listerning for tracking, enforcing disconnection for the listners
|
|
81
|
+
const rehydrateListener = BroadcastService.getMessageByEventName(TelemetryEvent.RehydrateMessageReceived).subscribe(() => {
|
|
82
|
+
startTracking = false;
|
|
83
|
+
stopTracking = false;
|
|
84
|
+
disconnectListener();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Rehydrate message is received when the widget is reloaded, this is to ensure that we are not tracking messages that are not part of the current conversation
|
|
88
|
+
// No need to keep listerning for tracking, enforcing disconnection for the listners
|
|
89
|
+
const historyListener = BroadcastService.getMessageByEventName(BroadcastEvent.HistoryMessageReceived).subscribe(() => {
|
|
90
|
+
startTracking = false;
|
|
91
|
+
stopTracking = false;
|
|
92
|
+
disconnectListener();
|
|
93
|
+
});
|
|
94
|
+
const offlineNetworkListener = BroadcastService.getMessageByEventName(TelemetryEvent.NetworkDisconnected).subscribe(() => {
|
|
95
|
+
startTracking = false;
|
|
96
|
+
stopTracking = false;
|
|
97
|
+
disconnectListener();
|
|
98
|
+
TelemetryHelper.logActionEvent(LogLevel.INFO, {
|
|
99
|
+
Event: TelemetryEvent.BotFirstMessageLapTrackError,
|
|
100
|
+
Description: "Tracker Stopped due to network disconnection"
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// this is to ensure that we are not tracking messages that are not part of the current conversation
|
|
105
|
+
const disconnectListener = () => {
|
|
106
|
+
historyListener.unsubscribe();
|
|
107
|
+
rehydrateListener.unsubscribe();
|
|
108
|
+
newMessageListener.unsubscribe();
|
|
109
|
+
widgetLoadListener.unsubscribe();
|
|
110
|
+
offlineNetworkListener.unsubscribe();
|
|
111
|
+
};
|
|
112
|
+
};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
2
2
|
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
|
|
3
3
|
function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
|
|
4
|
-
import { LogLevel, TelemetryEvent } from "../common/telemetry/TelemetryConstants";
|
|
4
|
+
import { BroadcastEvent, LogLevel, TelemetryEvent } from "../common/telemetry/TelemetryConstants";
|
|
5
|
+
import { BroadcastService } from "@microsoft/omnichannel-chat-components";
|
|
5
6
|
import { TelemetryHelper } from "../common/telemetry/TelemetryHelper";
|
|
6
7
|
export class FirstResponseLatencyTracker {
|
|
7
8
|
constructor() {
|
|
@@ -10,6 +11,32 @@ export class FirstResponseLatencyTracker {
|
|
|
10
11
|
_defineProperty(this, "isEnded", false);
|
|
11
12
|
_defineProperty(this, "startTrackingMessage", void 0);
|
|
12
13
|
_defineProperty(this, "stopTrackingMessage", void 0);
|
|
14
|
+
_defineProperty(this, "isReady", false);
|
|
15
|
+
_defineProperty(this, "offlineNetworkListener", BroadcastService.getMessageByEventName(TelemetryEvent.NetworkDisconnected).subscribe(() => {
|
|
16
|
+
this.isStarted = false;
|
|
17
|
+
this.isEnded = false;
|
|
18
|
+
TelemetryHelper.logActionEvent(LogLevel.INFO, {
|
|
19
|
+
Event: TelemetryEvent.MessageStopLapTrackError,
|
|
20
|
+
Description: "Tracker Stopped due to network disconnection"
|
|
21
|
+
});
|
|
22
|
+
}));
|
|
23
|
+
_defineProperty(this, "fmltrackingListener", BroadcastService.getMessageByEventName(BroadcastEvent.FMLTrackingCompleted).subscribe(() => {
|
|
24
|
+
this.isReady = true;
|
|
25
|
+
BroadcastService.postMessage({
|
|
26
|
+
eventName: BroadcastEvent.FMLTrackingCompletedAck,
|
|
27
|
+
payload: null
|
|
28
|
+
});
|
|
29
|
+
}));
|
|
30
|
+
// Rehydrate message is received when the widget is reloaded, this is to ensure that we are not tracking messages that are not part of the current conversation
|
|
31
|
+
// No need to keep listerning for tracking, enforcing disconnection for the listners
|
|
32
|
+
_defineProperty(this, "rehydrateListener", BroadcastService.getMessageByEventName(TelemetryEvent.RehydrateMessageReceived).subscribe(() => {
|
|
33
|
+
this.isReady = true;
|
|
34
|
+
}));
|
|
35
|
+
// Rehydrate message is received when the widget is reloaded, this is to ensure that we are not tracking messages that are not part of the current conversation
|
|
36
|
+
// No need to keep listerning for tracking, enforcing disconnection for the listners
|
|
37
|
+
_defineProperty(this, "historyListener", BroadcastService.getMessageByEventName(BroadcastEvent.HistoryMessageReceived).subscribe(() => {
|
|
38
|
+
this.isReady = true;
|
|
39
|
+
}));
|
|
13
40
|
// this is a workaround to ensure in reload we track effectively the messages
|
|
14
41
|
// we do have a mechanism in place to prevent log agent messages.
|
|
15
42
|
this.isABotConversation = true;
|
|
@@ -29,6 +56,7 @@ export class FirstResponseLatencyTracker {
|
|
|
29
56
|
|
|
30
57
|
// Tracking Functions
|
|
31
58
|
startTracking(payload) {
|
|
59
|
+
if (!this.isReady) return;
|
|
32
60
|
// this prevents to initiate tracking for multiple incoming messages
|
|
33
61
|
if (this.isStarted) {
|
|
34
62
|
return;
|
|
@@ -37,11 +65,9 @@ export class FirstResponseLatencyTracker {
|
|
|
37
65
|
if (!this.isABotConversation) {
|
|
38
66
|
return;
|
|
39
67
|
}
|
|
40
|
-
|
|
41
68
|
// control of states to prevent clashing of messages
|
|
42
69
|
this.isStarted = true;
|
|
43
70
|
this.isEnded = false;
|
|
44
|
-
|
|
45
71
|
// The idea of using types is to enrich telemetry data
|
|
46
72
|
this.startTrackingMessage = this.createTrackingMessage(payload, "userMessage");
|
|
47
73
|
}
|
|
@@ -93,9 +119,6 @@ export class FirstResponseLatencyTracker {
|
|
|
93
119
|
if (!payload || !payload.Id) {
|
|
94
120
|
throw new Error("Invalid payload");
|
|
95
121
|
}
|
|
96
|
-
// in the case of a reload, tracker will be paused, until last history message is received
|
|
97
|
-
// this is because we dont have a way to identidy send messages as part of the history
|
|
98
|
-
//if (this.inPause) return;
|
|
99
122
|
this.startTracking(payload);
|
|
100
123
|
} catch (e) {
|
|
101
124
|
TelemetryHelper.logActionEvent(LogLevel.ERROR, {
|
|
@@ -141,5 +164,9 @@ export class FirstResponseLatencyTracker {
|
|
|
141
164
|
this.isEnded = false;
|
|
142
165
|
this.startTrackingMessage = undefined;
|
|
143
166
|
this.stopTrackingMessage = undefined;
|
|
167
|
+
this.offlineNetworkListener.unsubscribe();
|
|
168
|
+
this.fmltrackingListener.unsubscribe();
|
|
169
|
+
this.rehydrateListener.unsubscribe();
|
|
170
|
+
this.historyListener.unsubscribe();
|
|
144
171
|
}
|
|
145
172
|
}
|
|
@@ -72,4 +72,16 @@ export const getScenarioType = activity => {
|
|
|
72
72
|
return ScenarioType.SystemMessageStrategy;
|
|
73
73
|
}
|
|
74
74
|
return ScenarioType.ReceivedMessageStrategy;
|
|
75
|
+
};
|
|
76
|
+
export const createTrackingMessage = (payload, type) => {
|
|
77
|
+
return {
|
|
78
|
+
Id: payload.Id,
|
|
79
|
+
role: payload.role,
|
|
80
|
+
timestamp: payload === null || payload === void 0 ? void 0 : payload.timestamp,
|
|
81
|
+
tags: payload.tags,
|
|
82
|
+
messageType: payload.messageType,
|
|
83
|
+
text: payload.text,
|
|
84
|
+
type: type,
|
|
85
|
+
checkTime: new Date().getTime()
|
|
86
|
+
};
|
|
75
87
|
};
|
|
@@ -43,6 +43,7 @@ export declare class FacadeChatSDK {
|
|
|
43
43
|
private enforceBase64Encoding;
|
|
44
44
|
private extractExpFromToken;
|
|
45
45
|
private setToken;
|
|
46
|
+
private corroborateTokenIsSet;
|
|
46
47
|
private tokenRing;
|
|
47
48
|
private validateAndExecuteCall;
|
|
48
49
|
initialize(optionalParams?: InitializeOptionalParams): Promise<ChatConfig>;
|
|
@@ -54,7 +54,9 @@ export declare enum BroadcastEvent {
|
|
|
54
54
|
UpdateConversationDataForTelemetry = "UpdateConversationDataForTelemetry",
|
|
55
55
|
ContactIdNotFound = "ContactIdNotFound",
|
|
56
56
|
SyncMinimize = "SyncMinimize",
|
|
57
|
-
OnWidgetError = "OnWidgetError"
|
|
57
|
+
OnWidgetError = "OnWidgetError",
|
|
58
|
+
FMLTrackingCompletedAck = "FMLTrackingCompletedAck",
|
|
59
|
+
FMLTrackingCompleted = "FMLTrackingCompleted"
|
|
58
60
|
}
|
|
59
61
|
export declare enum TelemetryEvent {
|
|
60
62
|
CallAdded = "CallAdded",
|
|
@@ -193,6 +195,8 @@ export declare enum TelemetryEvent {
|
|
|
193
195
|
MessageSent = "MessageSent",
|
|
194
196
|
MessageReceived = "MessageReceived",
|
|
195
197
|
MessageLapTrack = "MessageLapTrack",
|
|
198
|
+
BotFirstMessageLapTrack = "BotFirstMessageLapTrack",
|
|
199
|
+
BotFirstMessageLapTrackError = "BotFirstMessageLapTrackError",
|
|
196
200
|
MessageStartLapTrackError = "MessageStartLapTrackError",
|
|
197
201
|
MessageStopLapTrackError = "MessageStopLapTrackError",
|
|
198
202
|
SystemMessageReceived = "SystemMessageReceived",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const createTrackingForFirstMessage: () => void;
|
|
@@ -5,6 +5,7 @@ export declare class FirstResponseLatencyTracker {
|
|
|
5
5
|
private isEnded;
|
|
6
6
|
private startTrackingMessage?;
|
|
7
7
|
private stopTrackingMessage?;
|
|
8
|
+
private isReady;
|
|
8
9
|
constructor();
|
|
9
10
|
private createTrackingMessage;
|
|
10
11
|
private startTracking;
|
|
@@ -13,5 +14,9 @@ export declare class FirstResponseLatencyTracker {
|
|
|
13
14
|
private isMessageFromValidSender;
|
|
14
15
|
startClock(payload: MessagePayload): void;
|
|
15
16
|
stopClock(payload: MessagePayload): void;
|
|
17
|
+
private offlineNetworkListener;
|
|
18
|
+
private fmltrackingListener;
|
|
19
|
+
private rehydrateListener;
|
|
20
|
+
private historyListener;
|
|
16
21
|
private deregister;
|
|
17
22
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { MessagePayload, ScenarioType } from "./Constants";
|
|
1
|
+
import { MessagePayload, ScenarioType, TrackingMessage } from "./Constants";
|
|
2
2
|
import { IActivity } from "botframework-directlinejs";
|
|
3
3
|
export declare const isHistoryMessage: (activity: IActivity, startTime: number) => boolean;
|
|
4
4
|
export declare const buildMessagePayload: (activity: IActivity, userId: string) => MessagePayload;
|
|
5
5
|
export declare const polyfillMessagePayloadForEvent: (activity: IActivity, payload: MessagePayload, conversationId?: string) => MessagePayload;
|
|
6
6
|
export declare const getScenarioType: (activity: IActivity) => ScenarioType;
|
|
7
|
+
export declare const createTrackingMessage: (payload: MessagePayload, type: string) => TrackingMessage;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@microsoft/omnichannel-chat-widget",
|
|
3
|
-
"version": "1.7.8-main.
|
|
3
|
+
"version": "1.7.8-main.d71f599",
|
|
4
4
|
"description": "Microsoft Omnichannel Chat Widget",
|
|
5
5
|
"main": "lib/cjs/index.js",
|
|
6
6
|
"types": "lib/types/index.d.ts",
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
"babel-jest": "^27.3.1",
|
|
48
48
|
"babel-loader": "^8.2.3",
|
|
49
49
|
"botframework-directlinejs": "^0.15.0",
|
|
50
|
+
"concurrently": "^9.1.2",
|
|
50
51
|
"copyfiles": "^2.4.1",
|
|
51
52
|
"eslint": "^7.32.0",
|
|
52
53
|
"eslint-config-standard": "^16.0.3",
|
|
@@ -56,10 +57,12 @@
|
|
|
56
57
|
"eslint-plugin-react": "^7.26.1",
|
|
57
58
|
"eslint-plugin-storybook": "^0.5.3",
|
|
58
59
|
"file-loader": "^6.2.0",
|
|
60
|
+
"husky": "^9.1.7",
|
|
59
61
|
"jest": "^27.3.1",
|
|
60
62
|
"jest-dom": "^4.0.0",
|
|
61
63
|
"jest-image-snapshot": "^4.5.1",
|
|
62
64
|
"json": "^11.0.0",
|
|
65
|
+
"lint-staged": "^15.5.2",
|
|
63
66
|
"p-defer": "^4.0.1",
|
|
64
67
|
"playwright": "^1.20.0",
|
|
65
68
|
"postcss": "^8.3.9",
|
|
@@ -67,9 +70,11 @@
|
|
|
67
70
|
"react-docgen-typescript-plugin": "^1.0.8",
|
|
68
71
|
"react-dom": "^17.0.2",
|
|
69
72
|
"react-test-renderer": "^17.0.2",
|
|
73
|
+
"rimraf": "^6.0.1",
|
|
70
74
|
"storybook-addon-playwright": "^4.9.2",
|
|
71
75
|
"swiper": "^9.0.5",
|
|
72
76
|
"terser-webpack-plugin": "^4.2.3",
|
|
77
|
+
"thread-loader": "^2.1.3",
|
|
73
78
|
"ts-loader": "^9.2.6",
|
|
74
79
|
"typescript": "4.9.5",
|
|
75
80
|
"webpack": "^4.44.2",
|
|
@@ -97,7 +102,7 @@
|
|
|
97
102
|
"compose-storybook": "start-storybook -c stories/.storybook -p 9009",
|
|
98
103
|
"build-composite-storybook": "build-storybook -c stories/.storybook -o storybook-build",
|
|
99
104
|
"build-storybook": "build-storybook",
|
|
100
|
-
"build": "yarn lint && yarn build:esm && yarn build:cjs && tsc",
|
|
105
|
+
"build": "yarn clean yarn lint && yarn build:esm && yarn build:cjs && tsc",
|
|
101
106
|
"test:unit": "jest -c jest.config.unit.cjs --env=jsdom --runInBand --force-exit",
|
|
102
107
|
"test:e2e": "cd automation_tests && yarn test",
|
|
103
108
|
"test:e2e:build": "yarn build-sample && cd automation_tests && yarn test",
|
|
@@ -105,6 +110,8 @@
|
|
|
105
110
|
"test:all": "yarn test:unit && yarn test:visual",
|
|
106
111
|
"build:esm": "babel ./src --config-file ./babel.esm.config.json --out-dir lib/esm --extensions .ts,.js,.tsx --ignore **/*.test.ts,**/*.stories.tsx,**/*.test.tsx,**/*.spec.ts,**/*.spec.tsx",
|
|
107
112
|
"build:cjs": "babel ./src --config-file ./babel.config.json --out-dir lib/cjs --extensions .ts,.js,.tsx --ignore **/*.test.ts,**/*.stories.tsx,**/*.test.tsx,**/*.spec.ts,**/*.spec.tsx",
|
|
113
|
+
"build:esm:watch": "babel ./src --config-file ./babel.esm.config.json --out-dir lib/esm --extensions .ts,.js,.tsx --watch --ignore **/*.test.ts,**/*.stories.tsx,**/*.test.tsx,**/*.spec.ts,**/*.spec.tsx",
|
|
114
|
+
"build:cjs:watch": "babel ./src --config-file ./babel.config.json --out-dir lib/cjs --extensions .ts,.js,.tsx --watch --ignore **/*.test.ts,**/*.stories.tsx,**/*.test.tsx,**/*.spec.ts,**/*.spec.tsx",
|
|
108
115
|
"build:umd": "webpack --config ./webpack.umd.config.cjs",
|
|
109
116
|
"verify": "yarn install && yarn build-storybook && yarn test:all && yarn build && yarn storybook",
|
|
110
117
|
"testpack": "yarn build && yarn pack",
|
|
@@ -112,7 +119,10 @@
|
|
|
112
119
|
"build-sample:dev": "yarn build && webpack --config ./webpack.dev.config.cjs",
|
|
113
120
|
"test:visual:build": "yarn build-storybook && yarn test:visual",
|
|
114
121
|
"lint": "yarn eslint . --max-warnings=0",
|
|
115
|
-
"pretest:visual": "yarn playwright install"
|
|
122
|
+
"pretest:visual": "yarn playwright install",
|
|
123
|
+
"prepare": "husky install",
|
|
124
|
+
"clean": "rimraf lib",
|
|
125
|
+
"dev": "concurrently \"yarn build:esm:watch\" \"webpack --config ./webpack.config.cjs --watch\""
|
|
116
126
|
},
|
|
117
127
|
"resolutions": {
|
|
118
128
|
"**/url-parse": "1.5.9",
|