@microsoft/omnichannel-chat-widget 1.7.8-main.7a07fc5 → 1.7.8-main.ab4d3b4
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/telemetry/TelemetryConstants.js +9 -0
- package/lib/cjs/components/chatbuttonstateful/ChatButtonStateful.js +4 -5
- package/lib/cjs/components/headerstateful/HeaderStateful.js +3 -5
- package/lib/cjs/components/livechatwidget/common/ActivitySubscriber/BotAuthActivitySubscriber.js +4 -1
- package/lib/cjs/components/livechatwidget/common/chatDisconnectHelper.js +3 -1
- package/lib/cjs/components/livechatwidget/common/endChat.js +4 -18
- package/lib/cjs/components/livechatwidget/common/startChat.js +2 -0
- package/lib/cjs/components/livechatwidget/livechatwidgetstateful/LiveChatWidgetStateful.js +17 -11
- package/lib/cjs/components/ooohpanestateful/OOOHPaneStateful.js +6 -4
- package/lib/cjs/components/postchatsurveypanestateful/PostChatSurveyPaneStateful.js +22 -6
- package/lib/cjs/components/postchatsurveypanestateful/common/isValidSurveyUrl.js +28 -0
- package/lib/cjs/components/webchatcontainerstateful/WebChatContainerStateful.js +4 -3
- package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/timestamps/DeliveredTimestamp.js +4 -0
- package/lib/cjs/contexts/common/LiveChatWidgetContextInitialState.js +18 -6
- package/lib/cjs/firstresponselatency/Constants.js +13 -0
- package/lib/cjs/firstresponselatency/FirstMessageTrackerFromBot.js +118 -0
- package/lib/cjs/firstresponselatency/FirstResponseLatencyTracker.js +179 -0
- package/lib/cjs/firstresponselatency/util.js +98 -0
- package/lib/cjs/plugins/createChatTranscript.js +4 -4
- package/lib/cjs/plugins/newMessageEventHandler.js +102 -88
- package/lib/esm/common/telemetry/TelemetryConstants.js +9 -0
- package/lib/esm/components/chatbuttonstateful/ChatButtonStateful.js +4 -5
- package/lib/esm/components/headerstateful/HeaderStateful.js +3 -5
- package/lib/esm/components/livechatwidget/common/ActivitySubscriber/BotAuthActivitySubscriber.js +4 -1
- package/lib/esm/components/livechatwidget/common/chatDisconnectHelper.js +3 -1
- package/lib/esm/components/livechatwidget/common/endChat.js +4 -18
- package/lib/esm/components/livechatwidget/common/startChat.js +2 -0
- package/lib/esm/components/livechatwidget/livechatwidgetstateful/LiveChatWidgetStateful.js +17 -11
- package/lib/esm/components/ooohpanestateful/OOOHPaneStateful.js +6 -4
- package/lib/esm/components/postchatsurveypanestateful/PostChatSurveyPaneStateful.js +22 -6
- package/lib/esm/components/postchatsurveypanestateful/common/isValidSurveyUrl.js +21 -0
- package/lib/esm/components/webchatcontainerstateful/WebChatContainerStateful.js +4 -3
- package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/timestamps/DeliveredTimestamp.js +4 -0
- package/lib/esm/contexts/common/LiveChatWidgetContextInitialState.js +18 -6
- package/lib/esm/firstresponselatency/Constants.js +6 -0
- package/lib/esm/firstresponselatency/FirstMessageTrackerFromBot.js +112 -0
- package/lib/esm/firstresponselatency/FirstResponseLatencyTracker.js +172 -0
- package/lib/esm/firstresponselatency/util.js +87 -0
- package/lib/esm/plugins/createChatTranscript.js +4 -4
- package/lib/esm/plugins/newMessageEventHandler.js +100 -86
- package/lib/types/common/telemetry/TelemetryConstants.d.ts +10 -1
- package/lib/types/components/postchatsurveypanestateful/common/isValidSurveyUrl.d.ts +2 -0
- package/lib/types/contexts/common/ILiveChatWidgetContext.d.ts +1 -1
- package/lib/types/firstresponselatency/Constants.d.ts +30 -0
- package/lib/types/firstresponselatency/FirstMessageTrackerFromBot.d.ts +1 -0
- package/lib/types/firstresponselatency/FirstResponseLatencyTracker.d.ts +22 -0
- package/lib/types/firstresponselatency/util.d.ts +7 -0
- package/package.json +13 -3
|
@@ -0,0 +1,172 @@
|
|
|
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
|
+
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
|
|
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 { BroadcastEvent, LogLevel, TelemetryEvent } from "../common/telemetry/TelemetryConstants";
|
|
5
|
+
import { BroadcastService } from "@microsoft/omnichannel-chat-components";
|
|
6
|
+
import { TelemetryHelper } from "../common/telemetry/TelemetryHelper";
|
|
7
|
+
export class FirstResponseLatencyTracker {
|
|
8
|
+
constructor() {
|
|
9
|
+
_defineProperty(this, "isABotConversation", false);
|
|
10
|
+
_defineProperty(this, "isStarted", false);
|
|
11
|
+
_defineProperty(this, "isEnded", false);
|
|
12
|
+
_defineProperty(this, "startTrackingMessage", void 0);
|
|
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
|
+
}));
|
|
40
|
+
// this is a workaround to ensure in reload we track effectively the messages
|
|
41
|
+
// we do have a mechanism in place to prevent log agent messages.
|
|
42
|
+
this.isABotConversation = true;
|
|
43
|
+
}
|
|
44
|
+
createTrackingMessage(payload, type) {
|
|
45
|
+
return {
|
|
46
|
+
Id: payload.Id,
|
|
47
|
+
role: payload.role,
|
|
48
|
+
timestamp: payload === null || payload === void 0 ? void 0 : payload.timestamp,
|
|
49
|
+
tags: payload.tags,
|
|
50
|
+
messageType: payload.messageType,
|
|
51
|
+
text: payload.text,
|
|
52
|
+
type: type,
|
|
53
|
+
checkTime: new Date().getTime()
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Tracking Functions
|
|
58
|
+
startTracking(payload) {
|
|
59
|
+
if (!this.isReady) return;
|
|
60
|
+
// this prevents to initiate tracking for multiple incoming messages
|
|
61
|
+
if (this.isStarted) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
// this is to ensure we track only messages where bot is engaged
|
|
65
|
+
if (!this.isABotConversation) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
// control of states to prevent clashing of messages
|
|
69
|
+
this.isStarted = true;
|
|
70
|
+
this.isEnded = false;
|
|
71
|
+
// The idea of using types is to enrich telemetry data
|
|
72
|
+
this.startTrackingMessage = this.createTrackingMessage(payload, "userMessage");
|
|
73
|
+
}
|
|
74
|
+
handleAgentMessage(payload) {
|
|
75
|
+
var _payload$tags;
|
|
76
|
+
// this tag so far is only present in agent messages
|
|
77
|
+
if (payload !== null && payload !== void 0 && (_payload$tags = payload.tags) !== null && _payload$tags !== void 0 && _payload$tags.includes("public")) {
|
|
78
|
+
this.deregister();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
stopTracking(payload) {
|
|
82
|
+
var _this$stopTrackingMes, _this$startTrackingMe;
|
|
83
|
+
// this prevents execution for multiple incoming messages from the bot.
|
|
84
|
+
if (this.isEnded && !this.isStarted) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// control of states to prevent clashing of messages
|
|
89
|
+
this.isEnded = true;
|
|
90
|
+
this.isStarted = false;
|
|
91
|
+
|
|
92
|
+
// The idea of using types is to enrich telemetry data
|
|
93
|
+
this.stopTrackingMessage = this.createTrackingMessage(payload, "botMessage");
|
|
94
|
+
// calculating elapsed time
|
|
95
|
+
const elapsedTime = (((_this$stopTrackingMes = this.stopTrackingMessage) === null || _this$stopTrackingMes === void 0 ? void 0 : _this$stopTrackingMes.checkTime) ?? 0) - (((_this$startTrackingMe = this.startTrackingMessage) === null || _this$startTrackingMe === void 0 ? void 0 : _this$startTrackingMe.checkTime) ?? 0);
|
|
96
|
+
TelemetryHelper.logActionEvent(LogLevel.INFO, {
|
|
97
|
+
Event: TelemetryEvent.MessageLapTrack,
|
|
98
|
+
Description: "First response latency tracking",
|
|
99
|
+
CustomProperties: {
|
|
100
|
+
elapsedTime,
|
|
101
|
+
userMessage: this.startTrackingMessage,
|
|
102
|
+
botMessage: this.stopTrackingMessage
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// mechanism to ensure we track only allowed conversations
|
|
108
|
+
isMessageFromValidSender(payload) {
|
|
109
|
+
var _payload$tags2;
|
|
110
|
+
// agent scenario
|
|
111
|
+
if (payload !== null && payload !== void 0 && (_payload$tags2 = payload.tags) !== null && _payload$tags2 !== void 0 && _payload$tags2.includes("public")) {
|
|
112
|
+
this.handleAgentMessage(payload);
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
startClock(payload) {
|
|
118
|
+
try {
|
|
119
|
+
if (!payload || !payload.Id) {
|
|
120
|
+
throw new Error("Invalid payload");
|
|
121
|
+
}
|
|
122
|
+
this.startTracking(payload);
|
|
123
|
+
} catch (e) {
|
|
124
|
+
TelemetryHelper.logActionEvent(LogLevel.ERROR, {
|
|
125
|
+
Event: TelemetryEvent.MessageStartLapTrackError,
|
|
126
|
+
Description: "Error while starting the clock",
|
|
127
|
+
ExceptionDetails: e,
|
|
128
|
+
CustomProperties: {
|
|
129
|
+
payload: payload
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
stopClock(payload) {
|
|
135
|
+
try {
|
|
136
|
+
if (!payload || !payload.Id) {
|
|
137
|
+
throw new Error("Invalid payload");
|
|
138
|
+
}
|
|
139
|
+
if (!this.isMessageFromValidSender(payload)) return;
|
|
140
|
+
if (this.isABotConversation && this.isStarted) {
|
|
141
|
+
this.stopTracking(payload);
|
|
142
|
+
}
|
|
143
|
+
} catch (e) {
|
|
144
|
+
console.error("FRL : error while trying to stop the tracker", e);
|
|
145
|
+
TelemetryHelper.logActionEvent(LogLevel.ERROR, {
|
|
146
|
+
Event: TelemetryEvent.MessageStopLapTrackError,
|
|
147
|
+
Description: "Error while stopping the clock",
|
|
148
|
+
ExceptionDetails: e,
|
|
149
|
+
CustomProperties: {
|
|
150
|
+
payload: payload
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
//reset state
|
|
154
|
+
this.startTrackingMessage = undefined;
|
|
155
|
+
this.stopTrackingMessage = undefined;
|
|
156
|
+
this.isStarted = false;
|
|
157
|
+
this.isEnded = false;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
deregister() {
|
|
161
|
+
// Reset State
|
|
162
|
+
this.isABotConversation = false;
|
|
163
|
+
this.isStarted = false;
|
|
164
|
+
this.isEnded = false;
|
|
165
|
+
this.startTrackingMessage = undefined;
|
|
166
|
+
this.stopTrackingMessage = undefined;
|
|
167
|
+
this.offlineNetworkListener.unsubscribe();
|
|
168
|
+
this.fmltrackingListener.unsubscribe();
|
|
169
|
+
this.rehydrateListener.unsubscribe();
|
|
170
|
+
this.historyListener.unsubscribe();
|
|
171
|
+
}
|
|
172
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { ScenarioType } from "./Constants";
|
|
2
|
+
import { Constants } from "../common/Constants";
|
|
3
|
+
export const isHistoryMessage = (activity, startTime) => {
|
|
4
|
+
try {
|
|
5
|
+
if ((activity === null || activity === void 0 ? void 0 : activity.type) === Constants.message) {
|
|
6
|
+
var _activity$channelData, _activity$channelData2;
|
|
7
|
+
// this is an old piece of code, probably no longer relevant
|
|
8
|
+
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(Constants.historyMessageTag)) return true;
|
|
9
|
+
|
|
10
|
+
// Id is an epoch time in milliseconds , in utc format, for some reason is in a string format
|
|
11
|
+
if (activity !== null && activity !== void 0 && activity.id) {
|
|
12
|
+
/// activity.id is an string that contains epoch time in milliseconds
|
|
13
|
+
const activityId = parseInt(activity === null || activity === void 0 ? void 0 : activity.id);
|
|
14
|
+
|
|
15
|
+
// if the activity id is not a number, we default to new message
|
|
16
|
+
if (isNaN(activityId)) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// if the activity id is less than the start time, it means that the message is a history message
|
|
21
|
+
if (activityId < startTime) {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
// anything else will be considered a new message
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
} catch (e) {
|
|
29
|
+
// if there is an error in parsing the activity id, we will consider it a new message
|
|
30
|
+
console.error("Error in parsing activity id: ", e);
|
|
31
|
+
}
|
|
32
|
+
return false;
|
|
33
|
+
};
|
|
34
|
+
export const buildMessagePayload = (activity, userId) => {
|
|
35
|
+
var _text, _text2, _activity$channelData3, _activity$from;
|
|
36
|
+
return {
|
|
37
|
+
// To identify hidden contents vs empty content
|
|
38
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
39
|
+
text: (activity === null || activity === void 0 ? void 0 : (_text = activity.text) === null || _text === void 0 ? void 0 : _text.length) >= 1 ? `*contents hidden (${activity === null || activity === void 0 ? void 0 : (_text2 = activity.text) === null || _text2 === void 0 ? void 0 : _text2.length} chars)*` : "",
|
|
40
|
+
type: activity === null || activity === void 0 ? void 0 : activity.type,
|
|
41
|
+
timestamp: activity === null || activity === void 0 ? void 0 : activity.timestamp,
|
|
42
|
+
userId: userId,
|
|
43
|
+
tags: activity === null || activity === void 0 ? void 0 : (_activity$channelData3 = activity.channelData) === null || _activity$channelData3 === void 0 ? void 0 : _activity$channelData3.tags,
|
|
44
|
+
messageType: "",
|
|
45
|
+
Id: activity === null || activity === void 0 ? void 0 : activity.id,
|
|
46
|
+
role: activity === null || activity === void 0 ? void 0 : (_activity$from = activity.from) === null || _activity$from === void 0 ? void 0 : _activity$from.role,
|
|
47
|
+
isChatComplete: false
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
export const polyfillMessagePayloadForEvent = (activity, payload, conversationId) => {
|
|
51
|
+
var _activity$conversatio, _attachments, _activity$from2;
|
|
52
|
+
return {
|
|
53
|
+
...payload,
|
|
54
|
+
channelData: activity === null || activity === void 0 ? void 0 : activity.channelData,
|
|
55
|
+
chatId: activity === null || activity === void 0 ? void 0 : (_activity$conversatio = activity.conversation) === null || _activity$conversatio === void 0 ? void 0 : _activity$conversatio.id,
|
|
56
|
+
conversationId: conversationId,
|
|
57
|
+
Id: activity === null || activity === void 0 ? void 0 : activity.id,
|
|
58
|
+
isChatComplete: false,
|
|
59
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
60
|
+
text: activity === null || activity === void 0 ? void 0 : activity.text,
|
|
61
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
62
|
+
attachment: (activity === null || activity === void 0 ? void 0 : (_attachments = activity.attachments) === null || _attachments === void 0 ? void 0 : _attachments.length) >= 1 ? activity === null || activity === void 0 ? void 0 : activity.attachments : [],
|
|
63
|
+
role: activity === null || activity === void 0 ? void 0 : (_activity$from2 = activity.from) === null || _activity$from2 === void 0 ? void 0 : _activity$from2.role
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
export const getScenarioType = activity => {
|
|
67
|
+
var _activity$from3, _activity$channelData4, _activity$channelData5;
|
|
68
|
+
if ((activity === null || activity === void 0 ? void 0 : (_activity$from3 = activity.from) === null || _activity$from3 === void 0 ? void 0 : _activity$from3.role) === Constants.userMessageTag) {
|
|
69
|
+
return ScenarioType.UserSendMessageStrategy;
|
|
70
|
+
}
|
|
71
|
+
if (activity !== null && activity !== void 0 && (_activity$channelData4 = activity.channelData) !== null && _activity$channelData4 !== void 0 && (_activity$channelData5 = _activity$channelData4.tags) !== null && _activity$channelData5 !== void 0 && _activity$channelData5.includes(Constants.systemMessageTag)) {
|
|
72
|
+
return ScenarioType.SystemMessageStrategy;
|
|
73
|
+
}
|
|
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
|
+
};
|
|
87
|
+
};
|
|
@@ -168,7 +168,7 @@ class TranscriptHTMLBuilder {
|
|
|
168
168
|
<script>
|
|
169
169
|
class Translator {
|
|
170
170
|
static convertTranscriptMessageToActivity(message) {
|
|
171
|
-
const {created, OriginalMessageId, id, isControlMessage, content, tags, from, attachments, amsMetadata, amsReferences} = message;
|
|
171
|
+
const {created, OriginalMessageId, id, isControlMessage, content, tags, from, attachments, amsMetadata, amsReferences, amsreferences} = message;
|
|
172
172
|
|
|
173
173
|
//it's required to convert the id to a number, otherwise the webchat will not render the messages in the correct order
|
|
174
174
|
// if the OrginalMessageId is not present, we can use the id as the sequence id, which is always present.
|
|
@@ -215,7 +215,7 @@ class TranscriptHTMLBuilder {
|
|
|
215
215
|
}
|
|
216
216
|
|
|
217
217
|
// Attachments
|
|
218
|
-
if (amsReferences && amsMetadata) {
|
|
218
|
+
if ((amsReferences || amsreferences) && amsMetadata) {
|
|
219
219
|
const metadata = JSON.parse(amsMetadata);
|
|
220
220
|
const { fileName } = metadata[0];
|
|
221
221
|
const text = \`${this.attachmentMessage}\${fileName}\`;
|
|
@@ -239,7 +239,7 @@ class TranscriptHTMLBuilder {
|
|
|
239
239
|
// Message
|
|
240
240
|
if (content) {
|
|
241
241
|
// Adaptive card formatting
|
|
242
|
-
if (content.includes('"
|
|
242
|
+
if (content.includes('"attachments"') || content.includes('"suggestedActions"')) {
|
|
243
243
|
try {
|
|
244
244
|
const partialActivity = JSON.parse(content);
|
|
245
245
|
return {
|
|
@@ -678,7 +678,7 @@ const createChatTranscript = async function (transcript, facadeChatSDK) {
|
|
|
678
678
|
messages = await Promise.all(transcriptMessages.map(async message => {
|
|
679
679
|
// eslint-disable-line @typescript-eslint/no-explicit-any
|
|
680
680
|
const {
|
|
681
|
-
amsReferences,
|
|
681
|
+
amsReferences = message.amsreferences,
|
|
682
682
|
amsMetadata
|
|
683
683
|
} = message;
|
|
684
684
|
if (amsReferences && amsMetadata) {
|
|
@@ -1,100 +1,114 @@
|
|
|
1
1
|
import { BroadcastEvent, LogLevel, TelemetryEvent } from "../common/telemetry/TelemetryConstants";
|
|
2
|
+
import { ScenarioType } from "../firstresponselatency/Constants";
|
|
3
|
+
import { buildMessagePayload, getScenarioType, isHistoryMessage, polyfillMessagePayloadForEvent } from "../firstresponselatency/util";
|
|
2
4
|
import { BroadcastService } from "@microsoft/omnichannel-chat-components";
|
|
3
5
|
import { Constants } from "../common/Constants";
|
|
6
|
+
import { FirstResponseLatencyTracker } from "../firstresponselatency/FirstResponseLatencyTracker";
|
|
4
7
|
import { TelemetryHelper } from "../common/telemetry/TelemetryHelper";
|
|
5
8
|
import { TelemetryManager } from "../common/telemetry/TelemetryManager";
|
|
6
9
|
export const createOnNewAdapterActivityHandler = (chatId, userId) => {
|
|
10
|
+
// Hooking the message tracker in the listener, a bit invasive but easier to control.
|
|
11
|
+
const firstResponseLatencyTracker = new FirstResponseLatencyTracker();
|
|
12
|
+
// epoch time in utc for when start to listen.
|
|
13
|
+
// We dont longer have a mechanism to know if a message is history or new, so any message older than the time we start listening will be considered a history message.
|
|
14
|
+
// this is a workaround for the fact that we dont have a way to identify if a message is history or new, and it will provide consistency across different scenarios
|
|
15
|
+
const startTime = new Date().getTime();
|
|
16
|
+
let isHistoryMessageReceivedEventRaised = false;
|
|
7
17
|
const onNewAdapterActivityHandler = activity => {
|
|
8
|
-
|
|
9
|
-
const isActivityMessage = (activity === null || activity === void 0 ? void 0 : activity.type) === Constants.message;
|
|
10
|
-
const isHistoryMessage = isActivityMessage && ((activity === null || activity === void 0 ? void 0 : (_activity$channelData = activity.channelData) === null || _activity$channelData === void 0 ? void 0 : (_activity$channelData2 = _activity$channelData.tags) === null || _activity$channelData2 === void 0 ? void 0 : _activity$channelData2.includes(Constants.historyMessageTag)) || (activity === null || activity === void 0 ? void 0 : (_activity$channelData3 = activity.channelData) === null || _activity$channelData3 === void 0 ? void 0 : _activity$channelData3.fromList));
|
|
11
|
-
raiseMessageEvent(activity, isHistoryMessage);
|
|
18
|
+
raiseMessageEvent(activity);
|
|
12
19
|
};
|
|
13
|
-
|
|
14
|
-
|
|
20
|
+
const userSendMessageStrategy = activity => {
|
|
21
|
+
var _TelemetryManager$Int;
|
|
22
|
+
const payload = buildMessagePayload(activity, userId);
|
|
15
23
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
channelData: activity === null || activity === void 0 ? void 0 : activity.channelData,
|
|
21
|
-
chatId: activity === null || activity === void 0 ? void 0 : (_activity$conversatio = activity.conversation) === null || _activity$conversatio === void 0 ? void 0 : _activity$conversatio.id,
|
|
22
|
-
conversationId: (_TelemetryManager$Int = TelemetryManager.InternalTelemetryData) === null || _TelemetryManager$Int === void 0 ? void 0 : _TelemetryManager$Int.conversationId,
|
|
23
|
-
id: activity === null || activity === void 0 ? void 0 : activity.id,
|
|
24
|
-
isChatComplete: false,
|
|
25
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
26
|
-
text: activity === null || activity === void 0 ? void 0 : activity.text,
|
|
27
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
28
|
-
attachment: (activity === null || activity === void 0 ? void 0 : (_attachments = activity.attachments) === null || _attachments === void 0 ? void 0 : _attachments.length) >= 1 ? activity === null || activity === void 0 ? void 0 : activity.attachments : []
|
|
29
|
-
};
|
|
24
|
+
payload.messageType = Constants.userMessageTag;
|
|
25
|
+
const newMessageSentEvent = {
|
|
26
|
+
eventName: BroadcastEvent.NewMessageSent,
|
|
27
|
+
payload: polyfillMessagePayloadForEvent(activity, payload, (_TelemetryManager$Int = TelemetryManager.InternalTelemetryData) === null || _TelemetryManager$Int === void 0 ? void 0 : _TelemetryManager$Int.conversationId)
|
|
30
28
|
};
|
|
29
|
+
BroadcastService.postMessage(newMessageSentEvent);
|
|
30
|
+
if (!isHistoryMessage(activity, startTime)) {
|
|
31
|
+
firstResponseLatencyTracker.startClock(payload);
|
|
32
|
+
}
|
|
33
|
+
TelemetryHelper.logActionEvent(LogLevel.INFO, {
|
|
34
|
+
Event: TelemetryEvent.MessageSent,
|
|
35
|
+
Description: "New message sent"
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
const systemMessageStrategy = activity => {
|
|
39
|
+
const payload = buildMessagePayload(activity, userId);
|
|
40
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
41
|
+
payload.messageType = Constants.systemMessageTag;
|
|
42
|
+
TelemetryHelper.logActionEvent(LogLevel.INFO, {
|
|
43
|
+
Event: TelemetryEvent.SystemMessageReceived,
|
|
44
|
+
Description: "System message received"
|
|
45
|
+
});
|
|
46
|
+
};
|
|
47
|
+
const historyMessageStrategy = payload => {
|
|
48
|
+
const newMessageReceivedEvent = {
|
|
49
|
+
eventName: BroadcastEvent.HistoryMessageReceived,
|
|
50
|
+
payload: payload
|
|
51
|
+
};
|
|
52
|
+
BroadcastService.postMessage(newMessageReceivedEvent);
|
|
53
|
+
if (!isHistoryMessageReceivedEventRaised) {
|
|
54
|
+
// this is needed for reload scenarios, it helps to identify the last message received before the reload
|
|
55
|
+
isHistoryMessageReceivedEventRaised = true;
|
|
56
|
+
TelemetryHelper.logActionEvent(LogLevel.INFO, {
|
|
57
|
+
Event: TelemetryEvent.RehydrateMessageReceived,
|
|
58
|
+
Description: "History message received",
|
|
59
|
+
CustomProperties: payload
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
const isValidMessage = activity => {
|
|
64
|
+
var _activity$channelData, _activity$channelData2, _activity$channelData3;
|
|
65
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
66
|
+
const messageHasNoText = !(activity !== null && activity !== void 0 && activity.text);
|
|
67
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
68
|
+
const messageHasNoTags = !(activity !== null && activity !== void 0 && activity.channelData) || !(activity !== null && activity !== void 0 && (_activity$channelData = activity.channelData) !== null && _activity$channelData !== void 0 && _activity$channelData.tags) || (activity === null || activity === void 0 ? void 0 : (_activity$channelData2 = activity.channelData) === null || _activity$channelData2 === void 0 ? void 0 : (_activity$channelData3 = _activity$channelData2.tags) === null || _activity$channelData3 === void 0 ? void 0 : _activity$channelData3.length) === 0;
|
|
69
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
70
|
+
const messageHasNoAttachments = !(activity !== null && activity !== void 0 && activity.attachments) || (activity === null || activity === void 0 ? void 0 : activity.attachments.length) === 0;
|
|
71
|
+
if (messageHasNoTags && messageHasNoText && messageHasNoAttachments) {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
return true;
|
|
75
|
+
};
|
|
76
|
+
const receivedMessageStrategy = activity => {
|
|
77
|
+
var _TelemetryManager$Int3;
|
|
78
|
+
if (!isValidMessage(activity)) return;
|
|
79
|
+
const isHistoryMessageReceived = isHistoryMessage(activity, startTime);
|
|
80
|
+
const payload = buildMessagePayload(activity, userId);
|
|
81
|
+
payload.messageType = Constants.userMessageTag;
|
|
82
|
+
if (isHistoryMessageReceived) {
|
|
83
|
+
var _TelemetryManager$Int2;
|
|
84
|
+
historyMessageStrategy(polyfillMessagePayloadForEvent(activity, payload, (_TelemetryManager$Int2 = TelemetryManager.InternalTelemetryData) === null || _TelemetryManager$Int2 === void 0 ? void 0 : _TelemetryManager$Int2.conversationId));
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
firstResponseLatencyTracker.stopClock(payload);
|
|
88
|
+
const newMessageReceivedEvent = {
|
|
89
|
+
eventName: BroadcastEvent.NewMessageReceived,
|
|
90
|
+
payload: polyfillMessagePayloadForEvent(activity, payload, (_TelemetryManager$Int3 = TelemetryManager.InternalTelemetryData) === null || _TelemetryManager$Int3 === void 0 ? void 0 : _TelemetryManager$Int3.conversationId)
|
|
91
|
+
};
|
|
92
|
+
BroadcastService.postMessage(newMessageReceivedEvent);
|
|
93
|
+
TelemetryHelper.logActionEvent(LogLevel.INFO, {
|
|
94
|
+
Event: TelemetryEvent.MessageReceived,
|
|
95
|
+
Description: "New message received",
|
|
96
|
+
CustomProperties: payload
|
|
97
|
+
});
|
|
98
|
+
};
|
|
99
|
+
const raiseMessageEvent = activity => {
|
|
31
100
|
if ((activity === null || activity === void 0 ? void 0 : activity.type) === Constants.message) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
if ((activity === null || activity === void 0 ? void 0 : (_activity$from = activity.from) === null || _activity$from === void 0 ? void 0 : _activity$from.role) === Constants.userMessageTag) {
|
|
44
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
45
|
-
payload.messageType = Constants.userMessageTag;
|
|
46
|
-
const newMessageSentEvent = {
|
|
47
|
-
eventName: BroadcastEvent.NewMessageSent,
|
|
48
|
-
payload: polyfillMessagePayloadForEvent(payload)
|
|
49
|
-
};
|
|
50
|
-
BroadcastService.postMessage(newMessageSentEvent);
|
|
51
|
-
TelemetryHelper.logActionEvent(LogLevel.INFO, {
|
|
52
|
-
Event: TelemetryEvent.MessageSent,
|
|
53
|
-
Description: "New message sent"
|
|
54
|
-
});
|
|
55
|
-
} else {
|
|
56
|
-
var _activity$channelData5, _activity$channelData6;
|
|
57
|
-
if (activity !== null && activity !== void 0 && (_activity$channelData5 = activity.channelData) !== null && _activity$channelData5 !== void 0 && (_activity$channelData6 = _activity$channelData5.tags) !== null && _activity$channelData6 !== void 0 && _activity$channelData6.includes(Constants.systemMessageTag)) {
|
|
58
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
59
|
-
payload.messageType = Constants.systemMessageTag;
|
|
60
|
-
TelemetryHelper.logActionEvent(LogLevel.INFO, {
|
|
61
|
-
Event: TelemetryEvent.SystemMessageReceived,
|
|
62
|
-
Description: "System message received"
|
|
63
|
-
});
|
|
64
|
-
} else {
|
|
65
|
-
var _activity$channelData7, _activity$channelData8, _activity$channelData9;
|
|
66
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
67
|
-
const messageHasNoText = !(activity !== null && activity !== void 0 && activity.text);
|
|
68
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
69
|
-
const messageHasNoTags = !(activity !== null && activity !== void 0 && activity.channelData) || !(activity !== null && activity !== void 0 && (_activity$channelData7 = activity.channelData) !== null && _activity$channelData7 !== void 0 && _activity$channelData7.tags) || (activity === null || activity === void 0 ? void 0 : (_activity$channelData8 = activity.channelData) === null || _activity$channelData8 === void 0 ? void 0 : (_activity$channelData9 = _activity$channelData8.tags) === null || _activity$channelData9 === void 0 ? void 0 : _activity$channelData9.length) === 0;
|
|
70
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
71
|
-
const messageHasNoAttachments = !(activity !== null && activity !== void 0 && activity.attachments) || (activity === null || activity === void 0 ? void 0 : activity.attachments.length) === 0;
|
|
72
|
-
if (messageHasNoTags && messageHasNoText && messageHasNoAttachments) {
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
payload.messageType = Constants.userMessageTag;
|
|
76
|
-
}
|
|
77
|
-
const newMessageReceivedEvent = {
|
|
78
|
-
eventName: isHistoryMessage ? BroadcastEvent.HistoryMessageReceived : BroadcastEvent.NewMessageReceived,
|
|
79
|
-
payload: polyfillMessagePayloadForEvent(payload)
|
|
80
|
-
};
|
|
81
|
-
BroadcastService.postMessage(newMessageReceivedEvent);
|
|
82
|
-
if (!isHistoryMessage) {
|
|
83
|
-
TelemetryHelper.logActionEvent(LogLevel.INFO, {
|
|
84
|
-
Event: TelemetryEvent.MessageReceived,
|
|
85
|
-
Description: "New message received",
|
|
86
|
-
CustomProperties: payload
|
|
87
|
-
});
|
|
88
|
-
} else {
|
|
89
|
-
if (!isHistoryMessageReceivedEventRasied) {
|
|
90
|
-
isHistoryMessageReceivedEventRasied = true;
|
|
91
|
-
TelemetryHelper.logActionEvent(LogLevel.INFO, {
|
|
92
|
-
Event: TelemetryEvent.RehydrateMessageReceived,
|
|
93
|
-
Description: "History message received",
|
|
94
|
-
CustomProperties: payload
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
}
|
|
101
|
+
const scenarioType = getScenarioType(activity);
|
|
102
|
+
switch (scenarioType) {
|
|
103
|
+
case ScenarioType.UserSendMessageStrategy:
|
|
104
|
+
userSendMessageStrategy(activity);
|
|
105
|
+
break;
|
|
106
|
+
case ScenarioType.SystemMessageStrategy:
|
|
107
|
+
systemMessageStrategy(activity);
|
|
108
|
+
break;
|
|
109
|
+
case ScenarioType.ReceivedMessageStrategy:
|
|
110
|
+
receivedMessageStrategy(activity);
|
|
111
|
+
break;
|
|
98
112
|
}
|
|
99
113
|
}
|
|
100
114
|
};
|
|
@@ -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",
|
|
@@ -192,6 +194,11 @@ export declare enum TelemetryEvent {
|
|
|
192
194
|
ReconnectChatMinimize = "ReconnectChatMinimize",
|
|
193
195
|
MessageSent = "MessageSent",
|
|
194
196
|
MessageReceived = "MessageReceived",
|
|
197
|
+
MessageLapTrack = "MessageLapTrack",
|
|
198
|
+
BotFirstMessageLapTrack = "BotFirstMessageLapTrackError",
|
|
199
|
+
BotFirstMessageLapTrackError = "BotFirstMessageLapTrack",
|
|
200
|
+
MessageStartLapTrackError = "MessageStartLapTrackError",
|
|
201
|
+
MessageStopLapTrackError = "MessageStopLapTrackError",
|
|
195
202
|
SystemMessageReceived = "SystemMessageReceived",
|
|
196
203
|
RehydrateMessageReceived = "RehydrateMessageReceived",
|
|
197
204
|
CustomContextReceived = "CustomContextReceived",
|
|
@@ -206,6 +213,8 @@ export declare enum TelemetryEvent {
|
|
|
206
213
|
PostChatContextCallFailed = "PostChatContextCallFailed",
|
|
207
214
|
PostChatSurveyLoadingPaneLoaded = "PostChatSurveyLoadingPaneLoaded",
|
|
208
215
|
PostChatSurveyLoaded = "PostChatSurveyLoaded",
|
|
216
|
+
PostChatSurveyUrlValidationCompleted = "PostChatSurveyUrlValidationCompleted",
|
|
217
|
+
PostChatSurveyUrlValidationFailed = "PostChatSurveyUrlValidationFailed",
|
|
209
218
|
ChatDisconnectThreadEventReceived = "ChatDisconnectThreadEventReceived",
|
|
210
219
|
HiddenAdaptiveCardMessageReceived = "HiddenAdaptiveCardMessageReceived",
|
|
211
220
|
EndingAdapterAfterDisconnectionError = "EndingAdapterAfterDisconnectionError",
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import { ConfirmationState, ConversationEndEntity, ParticipantType } from "../../common/Constants";
|
|
1
2
|
import ChatConfig from "@microsoft/omnichannel-chat-sdk/lib/core/ChatConfig";
|
|
2
3
|
import { ConversationState } from "./ConversationState";
|
|
3
4
|
import { IInternalTelemetryData } from "../../common/telemetry/interfaces/IInternalTelemetryData";
|
|
4
5
|
import { ILiveChatWidgetLocalizedTexts } from "./ILiveChatWidgetLocalizedTexts";
|
|
5
6
|
import { IRenderingMiddlewareProps } from "../../components/webchatcontainerstateful/interfaces/IRenderingMiddlewareProps";
|
|
6
|
-
import { ConfirmationState, ConversationEndEntity, ParticipantType } from "../../common/Constants";
|
|
7
7
|
import { StartChatFailureType } from "./StartChatFailureType";
|
|
8
8
|
export interface ILiveChatWidgetContext {
|
|
9
9
|
domainStates: {
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export declare enum ScenarioType {
|
|
2
|
+
UserSendMessageStrategy = "UserSendMessageStrategy",
|
|
3
|
+
SystemMessageStrategy = "SystemMessageStrategy",
|
|
4
|
+
ReceivedMessageStrategy = "ReceivedMessageStrategy"
|
|
5
|
+
}
|
|
6
|
+
export type MessagePayload = {
|
|
7
|
+
text: string;
|
|
8
|
+
type: string;
|
|
9
|
+
timestamp?: string | undefined;
|
|
10
|
+
userId: string;
|
|
11
|
+
tags: string[];
|
|
12
|
+
messageType: string;
|
|
13
|
+
Id: string | undefined;
|
|
14
|
+
role: string | undefined;
|
|
15
|
+
channelData?: any;
|
|
16
|
+
chatId?: string;
|
|
17
|
+
conversationId?: string;
|
|
18
|
+
isChatComplete: boolean;
|
|
19
|
+
attachment?: any[];
|
|
20
|
+
};
|
|
21
|
+
export type TrackingMessage = {
|
|
22
|
+
Id: string | undefined;
|
|
23
|
+
role: string | undefined;
|
|
24
|
+
timestamp: string | undefined;
|
|
25
|
+
tags: string[];
|
|
26
|
+
messageType: string;
|
|
27
|
+
text: string;
|
|
28
|
+
checkTime?: number;
|
|
29
|
+
type: string;
|
|
30
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const createTrackingForFirstMessage: () => void;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { MessagePayload } from "./Constants";
|
|
2
|
+
export declare class FirstResponseLatencyTracker {
|
|
3
|
+
private isABotConversation;
|
|
4
|
+
private isStarted;
|
|
5
|
+
private isEnded;
|
|
6
|
+
private startTrackingMessage?;
|
|
7
|
+
private stopTrackingMessage?;
|
|
8
|
+
private isReady;
|
|
9
|
+
constructor();
|
|
10
|
+
private createTrackingMessage;
|
|
11
|
+
private startTracking;
|
|
12
|
+
private handleAgentMessage;
|
|
13
|
+
private stopTracking;
|
|
14
|
+
private isMessageFromValidSender;
|
|
15
|
+
startClock(payload: MessagePayload): void;
|
|
16
|
+
stopClock(payload: MessagePayload): void;
|
|
17
|
+
private offlineNetworkListener;
|
|
18
|
+
private fmltrackingListener;
|
|
19
|
+
private rehydrateListener;
|
|
20
|
+
private historyListener;
|
|
21
|
+
private deregister;
|
|
22
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { MessagePayload, ScenarioType, TrackingMessage } from "./Constants";
|
|
2
|
+
import { IActivity } from "botframework-directlinejs";
|
|
3
|
+
export declare const isHistoryMessage: (activity: IActivity, startTime: number) => boolean;
|
|
4
|
+
export declare const buildMessagePayload: (activity: IActivity, userId: string) => MessagePayload;
|
|
5
|
+
export declare const polyfillMessagePayloadForEvent: (activity: IActivity, payload: MessagePayload, conversationId?: string) => MessagePayload;
|
|
6
|
+
export declare const getScenarioType: (activity: IActivity) => ScenarioType;
|
|
7
|
+
export declare const createTrackingMessage: (payload: MessagePayload, type: string) => TrackingMessage;
|