wingbot 3.45.2 → 3.46.0-alpha.10
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/index.js +9 -3
- package/package.json +2 -2
- package/src/BotApp.js +7 -0
- package/src/BuildRouter.js +17 -2
- package/src/Processor.js +52 -21
- package/src/Request.js +17 -2
- package/src/Responder.js +11 -8
- package/src/ReturnSender.js +6 -2
- package/src/analytics/consts.js +110 -0
- package/src/analytics/onInteractionHandler.js +221 -87
- package/src/flags.js +7 -1
- package/src/utils/quickReplies.js +3 -3
- package/src/utils/stateVariables.js +6 -3
package/index.js
CHANGED
|
@@ -52,7 +52,11 @@ const {
|
|
|
52
52
|
bufferloader,
|
|
53
53
|
MemoryStateStorage
|
|
54
54
|
} = require('./src/tools');
|
|
55
|
-
const
|
|
55
|
+
const {
|
|
56
|
+
TrackingCategory,
|
|
57
|
+
TrackingType,
|
|
58
|
+
ResponseFlag
|
|
59
|
+
} = require('./src/analytics/consts');
|
|
56
60
|
|
|
57
61
|
const { version: wingbotVersion } = require('./package.json');
|
|
58
62
|
|
|
@@ -122,8 +126,10 @@ module.exports = {
|
|
|
122
126
|
// tests
|
|
123
127
|
ConversationTester,
|
|
124
128
|
|
|
125
|
-
// flags
|
|
126
|
-
|
|
129
|
+
// flags & tracking
|
|
130
|
+
TrackingCategory,
|
|
131
|
+
TrackingType,
|
|
132
|
+
ResponseFlag,
|
|
127
133
|
|
|
128
134
|
wingbotVersion,
|
|
129
135
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wingbot",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.46.0-alpha.10",
|
|
4
4
|
"description": "Enterprise Messaging Bot Conversation Engine",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"doc": "npm run doc:gql && node ./bin/makeApiDoc.js && cpy ./CHANGELOG.md ./doc && gitbook install ./doc && gitbook build ./doc && rimraf -rf ./docs && rimraf --rf ./doc/CHANGELOG.md && move-cli ./doc/_book ./docs",
|
|
10
10
|
"test": "npm run test:lint && npm run test:coverage && npm run test:coverage:threshold",
|
|
11
11
|
"test:coverage": "nyc --reporter=html mocha ./test && nyc report",
|
|
12
|
-
"test:coverage:threshold": "nyc check-coverage --lines 90 --functions
|
|
12
|
+
"test:coverage:threshold": "nyc check-coverage --lines 90 --functions 89 --branches 80",
|
|
13
13
|
"test:backend": "mocha ./test",
|
|
14
14
|
"test:lint": "eslint --ext .js src test *.js plugins"
|
|
15
15
|
},
|
package/src/BotApp.js
CHANGED
|
@@ -116,6 +116,8 @@ class BotApp {
|
|
|
116
116
|
this._senderLogger = chatLogStorage;
|
|
117
117
|
this._verify = promisify(jwt.verify);
|
|
118
118
|
|
|
119
|
+
this._bot = bot;
|
|
120
|
+
|
|
119
121
|
this._processor = new Processor(bot, {
|
|
120
122
|
...processorOptions,
|
|
121
123
|
secret,
|
|
@@ -202,9 +204,14 @@ class BotApp {
|
|
|
202
204
|
analyticsStorage.setDefaultLogger(log);
|
|
203
205
|
}
|
|
204
206
|
|
|
207
|
+
// @ts-ignore
|
|
208
|
+
const { snapshot = null, botId = null } = this._bot;
|
|
209
|
+
|
|
205
210
|
const { onInteraction, onEvent } = onInteractionHandler({
|
|
206
211
|
log,
|
|
207
212
|
anonymize: this._textFilter,
|
|
213
|
+
snapshot,
|
|
214
|
+
botId,
|
|
208
215
|
...options
|
|
209
216
|
}, analyticsStorage);
|
|
210
217
|
|
package/src/BuildRouter.js
CHANGED
|
@@ -39,6 +39,7 @@ const MESSAGE_RESOLVER_NAME = 'botbuild.message';
|
|
|
39
39
|
* @typedef {object} Route
|
|
40
40
|
* @prop {number} id
|
|
41
41
|
* @prop {string|null} path
|
|
42
|
+
* @prop {string|null} [skill]
|
|
42
43
|
* @prop {Resolver[]} resolvers
|
|
43
44
|
* @prop {boolean} [isFallback]
|
|
44
45
|
* @prop {string[]} [aiTags]
|
|
@@ -238,6 +239,14 @@ class BuildRouter extends Router {
|
|
|
238
239
|
}
|
|
239
240
|
}
|
|
240
241
|
|
|
242
|
+
get snapshot () {
|
|
243
|
+
return this._snapshot;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
get botId () {
|
|
247
|
+
return this._botId;
|
|
248
|
+
}
|
|
249
|
+
|
|
241
250
|
/**
|
|
242
251
|
* @returns {C}
|
|
243
252
|
*/
|
|
@@ -674,9 +683,10 @@ class BuildRouter extends Router {
|
|
|
674
683
|
*
|
|
675
684
|
* @param {TransformedRoute} route
|
|
676
685
|
* @param {boolean} nextRouteIsSameResponder
|
|
686
|
+
* @param {string} includedBlockId
|
|
677
687
|
* @returns {Middleware<S,C>[]}
|
|
678
688
|
*/
|
|
679
|
-
_buildRouteHead (route, nextRouteIsSameResponder) {
|
|
689
|
+
_buildRouteHead (route, nextRouteIsSameResponder, includedBlockId) {
|
|
680
690
|
const resolvers = [];
|
|
681
691
|
|
|
682
692
|
if (!route.isFallback) {
|
|
@@ -712,6 +722,11 @@ class BuildRouter extends Router {
|
|
|
712
722
|
if (bounceResolver) {
|
|
713
723
|
resolvers.push(bounceResolver);
|
|
714
724
|
}
|
|
725
|
+
} else if (!includedBlockId && route.skill) {
|
|
726
|
+
resolvers.push((req, res) => {
|
|
727
|
+
res.trackAsSkill(route.skill);
|
|
728
|
+
return Router.CONTINUE;
|
|
729
|
+
});
|
|
715
730
|
}
|
|
716
731
|
}
|
|
717
732
|
|
|
@@ -752,7 +767,7 @@ class BuildRouter extends Router {
|
|
|
752
767
|
};
|
|
753
768
|
|
|
754
769
|
const resolvers = [
|
|
755
|
-
...this._buildRouteHead(route, nextRouteIsSameResponder),
|
|
770
|
+
...this._buildRouteHead(route, nextRouteIsSameResponder, includedBlockId),
|
|
756
771
|
...this.buildResolvers(route.resolvers, route, buildInfo)
|
|
757
772
|
];
|
|
758
773
|
|
package/src/Processor.js
CHANGED
|
@@ -16,6 +16,9 @@ const { mergeState, isUserInteraction } = require('./utils/stateVariables');
|
|
|
16
16
|
/** @typedef {import('./ReducerWrapper')} ReducerWrapper */
|
|
17
17
|
/** @typedef {import('./Router')} Router */
|
|
18
18
|
/** @typedef {import('./BuildRouter')} BuildRouter */
|
|
19
|
+
/** @typedef {import('./analytics/consts').TrackingCategory} TrackingCategory */
|
|
20
|
+
/** @typedef {import('./analytics/consts').TrackingType} TrackingType */
|
|
21
|
+
/** @typedef {import('./analytics/consts').ResponseFlag} ResponseFlag */
|
|
19
22
|
|
|
20
23
|
/**
|
|
21
24
|
* @typedef {object} AutoTypingConfig
|
|
@@ -35,8 +38,8 @@ const { mergeState, isUserInteraction } = require('./utils/stateVariables');
|
|
|
35
38
|
|
|
36
39
|
/**
|
|
37
40
|
* @typedef {object} TrackingEvent
|
|
38
|
-
* @prop {
|
|
39
|
-
* @prop {
|
|
41
|
+
* @prop {TrackingType} type
|
|
42
|
+
* @prop {TrackingCategory} category
|
|
40
43
|
* @prop {string} action
|
|
41
44
|
* @prop {string} label
|
|
42
45
|
* @prop {number} value
|
|
@@ -55,7 +58,10 @@ const { mergeState, isUserInteraction } = require('./utils/stateVariables');
|
|
|
55
58
|
* @prop {object} state
|
|
56
59
|
* @prop {object} data
|
|
57
60
|
* @prop {string|null} skill
|
|
58
|
-
* @prop {TrackingObject} tracking
|
|
61
|
+
* @prop {TrackingObject} tracking - deprecated
|
|
62
|
+
* @prop {TrackingEvent[]} events
|
|
63
|
+
* @prop {ResponseFlag|null} flag
|
|
64
|
+
* @prop {boolean} nonInteractive
|
|
59
65
|
*/
|
|
60
66
|
|
|
61
67
|
/**
|
|
@@ -134,6 +140,19 @@ function NAME_FROM_STATE (state) {
|
|
|
134
140
|
}
|
|
135
141
|
|
|
136
142
|
const MAX_TS = 9999999999999;
|
|
143
|
+
const CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
|
|
144
|
+
|
|
145
|
+
function toBase (number) {
|
|
146
|
+
let result = '';
|
|
147
|
+
let integer = number;
|
|
148
|
+
|
|
149
|
+
do {
|
|
150
|
+
result = CHARS[integer % 62] + result;
|
|
151
|
+
integer = Math.floor(integer / 62);
|
|
152
|
+
} while (integer > 0);
|
|
153
|
+
|
|
154
|
+
return result;
|
|
155
|
+
}
|
|
137
156
|
|
|
138
157
|
/**
|
|
139
158
|
* Messaging event processor
|
|
@@ -381,7 +400,7 @@ class Processor extends EventEmitter {
|
|
|
381
400
|
} = await this
|
|
382
401
|
._processMessage(message, pageId, messageSender, responderData, preloadPromise));
|
|
383
402
|
|
|
384
|
-
await this._emitInteractionEvent(req, messageSender, state, data);
|
|
403
|
+
await this._emitInteractionEvent(req, res, messageSender, state, data);
|
|
385
404
|
|
|
386
405
|
return messageSender.finished(req, res, null, errorHandler);
|
|
387
406
|
} catch (e) {
|
|
@@ -394,12 +413,13 @@ class Processor extends EventEmitter {
|
|
|
394
413
|
/**
|
|
395
414
|
*
|
|
396
415
|
* @param {Request} req
|
|
416
|
+
* @param {Responder} res
|
|
397
417
|
* @param {ReturnSender} messageSender
|
|
398
418
|
* @param {object} state
|
|
399
419
|
* @param {object} data
|
|
400
420
|
* @returns {Promise}
|
|
401
421
|
*/
|
|
402
|
-
_emitInteractionEvent (req, messageSender, state, data) {
|
|
422
|
+
_emitInteractionEvent (req, res, messageSender, state, data) {
|
|
403
423
|
const shouldNotTrack = data._initialEventShouldNotBeTracked === true;
|
|
404
424
|
|
|
405
425
|
if (shouldNotTrack) {
|
|
@@ -408,7 +428,10 @@ class Processor extends EventEmitter {
|
|
|
408
428
|
|
|
409
429
|
const { _lastAction: lastAction = null } = req.state;
|
|
410
430
|
const actions = messageSender.visitedInteractions;
|
|
411
|
-
const skill =
|
|
431
|
+
const skill = typeof res.newState._trackAsSkill === 'undefined'
|
|
432
|
+
? (req.state._trackAsSkill || null)
|
|
433
|
+
: res.newState._trackAsSkill;
|
|
434
|
+
const { events = [] } = messageSender.tracking;
|
|
412
435
|
|
|
413
436
|
const event = {
|
|
414
437
|
req,
|
|
@@ -417,7 +440,10 @@ class Processor extends EventEmitter {
|
|
|
417
440
|
state,
|
|
418
441
|
data,
|
|
419
442
|
skill,
|
|
420
|
-
tracking: messageSender.tracking
|
|
443
|
+
tracking: messageSender.tracking,
|
|
444
|
+
events,
|
|
445
|
+
flag: res.senderMeta.flag,
|
|
446
|
+
nonInteractive: !isUserInteraction(req)
|
|
421
447
|
};
|
|
422
448
|
|
|
423
449
|
return Promise.allSettled([
|
|
@@ -575,27 +601,35 @@ class Processor extends EventEmitter {
|
|
|
575
601
|
let {
|
|
576
602
|
_sct: sessionCount = 0,
|
|
577
603
|
_sid: sessionId = null,
|
|
578
|
-
|
|
604
|
+
_sst: sessionStart = 0,
|
|
605
|
+
_sts: sessionTs = (state._segStamp || 0),
|
|
579
606
|
_snew: sessionCreated
|
|
580
607
|
} = state;
|
|
581
608
|
|
|
609
|
+
const interactive = isUserInteraction(req);
|
|
610
|
+
|
|
582
611
|
if ((isUserInteraction(req)
|
|
583
|
-
&& (
|
|
612
|
+
&& (sessionTs + this.options.sessionDuration) < Date.now())
|
|
584
613
|
|| !sessionId) {
|
|
585
614
|
|
|
615
|
+
sessionStart = timestamp;
|
|
616
|
+
sessionTs = timestamp;
|
|
586
617
|
sessionId = Processor._createSessionId(req.pageId, req.senderId, timestamp);
|
|
587
618
|
sessionCount++;
|
|
588
619
|
sessionCreated = true;
|
|
589
620
|
} else {
|
|
590
621
|
sessionCreated = false;
|
|
591
|
-
}
|
|
592
622
|
|
|
593
|
-
|
|
623
|
+
if (interactive) {
|
|
624
|
+
sessionTs = timestamp;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
594
627
|
|
|
595
628
|
Object.assign(state, {
|
|
596
629
|
_sct: sessionCount,
|
|
597
630
|
_sid: sessionId,
|
|
598
|
-
|
|
631
|
+
_sst: sessionStart,
|
|
632
|
+
_sts: sessionTs,
|
|
599
633
|
_snew: sessionCreated
|
|
600
634
|
});
|
|
601
635
|
} else {
|
|
@@ -785,21 +819,18 @@ class Processor extends EventEmitter {
|
|
|
785
819
|
.digest('hex');
|
|
786
820
|
|
|
787
821
|
return senderHash.match(/[a-f0-9]{1,13}/g)
|
|
788
|
-
.map((v) => parseInt(v, 16)
|
|
822
|
+
.map((v) => toBase(parseInt(v, 16)))
|
|
789
823
|
.join('');
|
|
790
824
|
}
|
|
791
825
|
|
|
792
826
|
static _createSessionId (pageId, senderId, timestamp = Date.now()) {
|
|
793
|
-
const senderShort = Processor._shakeShort(`${senderId}|${pageId}}`,
|
|
827
|
+
const senderShort = Processor._shakeShort(`${senderId}|${pageId}}`, 12);
|
|
794
828
|
|
|
795
|
-
const rand = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER)
|
|
796
|
-
.toString(36);
|
|
829
|
+
const rand = toBase(Math.floor(Math.random() * Number.MAX_SAFE_INTEGER));
|
|
797
830
|
|
|
798
|
-
const randTS = Math.floor(Date.now() %
|
|
799
|
-
.toString(36);
|
|
831
|
+
const randTS = toBase(Math.floor(Date.now() % 10000));
|
|
800
832
|
|
|
801
|
-
const ts = Math.floor(MAX_TS - timestamp)
|
|
802
|
-
.toString(36);
|
|
833
|
+
const ts = toBase(Math.floor(MAX_TS - timestamp));
|
|
803
834
|
|
|
804
835
|
// console.log({
|
|
805
836
|
// base: `${ts}.${senderShort}`.length,
|
|
@@ -811,7 +842,7 @@ class Processor extends EventEmitter {
|
|
|
811
842
|
// });
|
|
812
843
|
|
|
813
844
|
return `${ts}.${senderShort}`
|
|
814
|
-
.padEnd(
|
|
845
|
+
.padEnd(28, randTS)
|
|
815
846
|
.padEnd(32, rand);
|
|
816
847
|
}
|
|
817
848
|
|
package/src/Request.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
const Ai = require('./Ai');
|
|
7
7
|
const { tokenize, parseActionPayload } = require('./utils');
|
|
8
8
|
const { quickReplyAction } = require('./utils/quickReplies');
|
|
9
|
-
const {
|
|
9
|
+
const { ResponseFlag } = require('./analytics/consts');
|
|
10
10
|
const { getSetState } = require('./utils/getUpdate');
|
|
11
11
|
const { vars, checkSetState } = require('./utils/stateVariables');
|
|
12
12
|
const OrchestratorClient = require('./OrchestratorClient');
|
|
@@ -391,7 +391,7 @@ class Request {
|
|
|
391
391
|
data: {
|
|
392
392
|
...data,
|
|
393
393
|
_senderMeta: {
|
|
394
|
-
flag:
|
|
394
|
+
flag: ResponseFlag.DISAMBIGUATION_SELECTED,
|
|
395
395
|
likelyIntent: intent.intent,
|
|
396
396
|
disambText: text
|
|
397
397
|
}
|
|
@@ -801,6 +801,7 @@ class Request {
|
|
|
801
801
|
* @returns {Action|null}
|
|
802
802
|
*/
|
|
803
803
|
expected () {
|
|
804
|
+
// @ts-ignore
|
|
804
805
|
return this.state._expected || null;
|
|
805
806
|
}
|
|
806
807
|
|
|
@@ -816,6 +817,7 @@ class Request {
|
|
|
816
817
|
*/
|
|
817
818
|
expectedKeywords (justOnce = false) {
|
|
818
819
|
const {
|
|
820
|
+
// @ts-ignore
|
|
819
821
|
_expectedKeywords: exKeywords
|
|
820
822
|
} = this.state;
|
|
821
823
|
|
|
@@ -855,7 +857,9 @@ class Request {
|
|
|
855
857
|
*/
|
|
856
858
|
expectedContext (justOnce = false, includeKeywords = false) {
|
|
857
859
|
const ad = this.actionData();
|
|
860
|
+
// @ts-ignore
|
|
858
861
|
const expected = ad._useExpected || this.state._expected;
|
|
862
|
+
// @ts-ignore
|
|
859
863
|
const confident = this.state._expectedConfidentInput;
|
|
860
864
|
|
|
861
865
|
const ret = {};
|
|
@@ -1097,6 +1101,7 @@ class Request {
|
|
|
1097
1101
|
* @returns {boolean}
|
|
1098
1102
|
*/
|
|
1099
1103
|
isConfidentInput () {
|
|
1104
|
+
// @ts-ignore
|
|
1100
1105
|
return this.state._expectedConfidentInput === true;
|
|
1101
1106
|
}
|
|
1102
1107
|
|
|
@@ -1123,11 +1128,15 @@ class Request {
|
|
|
1123
1128
|
res = parseActionPayload(this.message.quick_reply);
|
|
1124
1129
|
}
|
|
1125
1130
|
|
|
1131
|
+
// @ts-ignore
|
|
1126
1132
|
if (!res && this.state._expectedKeywords) {
|
|
1133
|
+
// @ts-ignore
|
|
1127
1134
|
res = this._actionByExpectedKeywords(this.state._expected);
|
|
1128
1135
|
}
|
|
1129
1136
|
|
|
1137
|
+
// @ts-ignore
|
|
1130
1138
|
if (!res && this.state._expected) {
|
|
1139
|
+
// @ts-ignore
|
|
1131
1140
|
res = parseActionPayload(this.state._expected);
|
|
1132
1141
|
}
|
|
1133
1142
|
|
|
@@ -1215,7 +1224,9 @@ class Request {
|
|
|
1215
1224
|
}
|
|
1216
1225
|
|
|
1217
1226
|
_getLocalPathRegexp () {
|
|
1227
|
+
// @ts-ignore
|
|
1218
1228
|
if (this.state._lastVisitedPath) {
|
|
1229
|
+
// @ts-ignore
|
|
1219
1230
|
return new RegExp(`^${this.state._lastVisitedPath}/[^/]+`);
|
|
1220
1231
|
}
|
|
1221
1232
|
let expected = this.expected();
|
|
@@ -1295,6 +1306,7 @@ class Request {
|
|
|
1295
1306
|
}
|
|
1296
1307
|
|
|
1297
1308
|
_actionByExpectedKeywords (expected) {
|
|
1309
|
+
// @ts-ignore
|
|
1298
1310
|
if (!this.state._expectedKeywords) {
|
|
1299
1311
|
return null;
|
|
1300
1312
|
}
|
|
@@ -1315,8 +1327,10 @@ class Request {
|
|
|
1315
1327
|
|
|
1316
1328
|
_resolveQuickReplyActions () {
|
|
1317
1329
|
if (this._quickReplyActions === null) {
|
|
1330
|
+
// @ts-ignore
|
|
1318
1331
|
if (this.state._expectedKeywords) {
|
|
1319
1332
|
this._quickReplyActions = quickReplyAction(
|
|
1333
|
+
// @ts-ignore
|
|
1320
1334
|
this.state._expectedKeywords,
|
|
1321
1335
|
this,
|
|
1322
1336
|
Ai.ai
|
|
@@ -1377,6 +1391,7 @@ class Request {
|
|
|
1377
1391
|
*/
|
|
1378
1392
|
expectedEntities () {
|
|
1379
1393
|
const {
|
|
1394
|
+
// @ts-ignore
|
|
1380
1395
|
_expectedKeywords: exKeywords
|
|
1381
1396
|
} = this.state;
|
|
1382
1397
|
|
package/src/Responder.js
CHANGED
|
@@ -9,7 +9,7 @@ const ButtonTemplate = require('./templates/ButtonTemplate');
|
|
|
9
9
|
const GenericTemplate = require('./templates/GenericTemplate');
|
|
10
10
|
const ListTemplate = require('./templates/ListTemplate');
|
|
11
11
|
const { makeAbsolute, makeQuickReplies } = require('./utils');
|
|
12
|
-
const {
|
|
12
|
+
const { ResponseFlag } = require('./analytics/consts');
|
|
13
13
|
const { checkSetState } = require('./utils/stateVariables');
|
|
14
14
|
const {
|
|
15
15
|
FEATURE_VOICE,
|
|
@@ -23,6 +23,9 @@ const TYPE_MESSAGE_TAG = 'MESSAGE_TAG';
|
|
|
23
23
|
const EXCEPTION_HOPCOUNT_THRESHOLD = 5;
|
|
24
24
|
|
|
25
25
|
/** @typedef {import('./Request')} Request */
|
|
26
|
+
/** @typedef {import('./ReturnSender').UploadResult} UploadResult */
|
|
27
|
+
/** @typedef {import('./analytics/consts').TrackingCategory} TrackingCategory */
|
|
28
|
+
/** @typedef {import('./analytics/consts').TrackingType} TrackingType */
|
|
26
29
|
|
|
27
30
|
/**
|
|
28
31
|
* @enum {string} ExpectedInput
|
|
@@ -48,7 +51,7 @@ Object.freeze(ExpectedInput);
|
|
|
48
51
|
|
|
49
52
|
/**
|
|
50
53
|
* @typedef {object} SenderMeta
|
|
51
|
-
* @prop {
|
|
54
|
+
* @prop {ResponseFlag|null} flag
|
|
52
55
|
* @prop {string} [likelyIntent]
|
|
53
56
|
* @prop {string} [disambText]
|
|
54
57
|
* @prop {string[]} [disambiguationIntents]
|
|
@@ -71,8 +74,6 @@ Object.freeze(ExpectedInput);
|
|
|
71
74
|
* @returns {VoiceControl}
|
|
72
75
|
*/
|
|
73
76
|
|
|
74
|
-
/** @typedef {import('./ReturnSender').UploadResult} UploadResult */
|
|
75
|
-
|
|
76
77
|
/**
|
|
77
78
|
* Instance of responder is passed as second parameter of handler (res)
|
|
78
79
|
*
|
|
@@ -200,7 +201,7 @@ class Responder {
|
|
|
200
201
|
* @returns {this}
|
|
201
202
|
*/
|
|
202
203
|
doNotLogTheEvent () {
|
|
203
|
-
this._senderMeta = { flag:
|
|
204
|
+
this._senderMeta = { flag: ResponseFlag.DO_NOT_LOG };
|
|
204
205
|
return this;
|
|
205
206
|
}
|
|
206
207
|
|
|
@@ -209,8 +210,8 @@ class Responder {
|
|
|
209
210
|
* Events are aggregated within ReturnSender and can be caught
|
|
210
211
|
* within Processor's `interaction` event (event.tracking.events)
|
|
211
212
|
*
|
|
212
|
-
* @param {
|
|
213
|
-
* @param {
|
|
213
|
+
* @param {TrackingType} type - (log,report,conversation,audit,user,training)
|
|
214
|
+
* @param {TrackingCategory} category
|
|
214
215
|
* @param {string} [action]
|
|
215
216
|
* @param {string} [label]
|
|
216
217
|
* @param {number} [value]
|
|
@@ -471,7 +472,7 @@ class Responder {
|
|
|
471
472
|
|
|
472
473
|
if (disambiguationIntents.length > 0) {
|
|
473
474
|
this._senderMeta = {
|
|
474
|
-
flag:
|
|
475
|
+
flag: ResponseFlag.DISAMBIGUATION_OFFERED,
|
|
475
476
|
disambiguationIntents
|
|
476
477
|
};
|
|
477
478
|
}
|
|
@@ -1015,6 +1016,8 @@ class Responder {
|
|
|
1015
1016
|
$hopCount++;
|
|
1016
1017
|
}
|
|
1017
1018
|
|
|
1019
|
+
this._senderMeta = { flag: ResponseFlag.HANDOVER };
|
|
1020
|
+
|
|
1018
1021
|
if (data === null) {
|
|
1019
1022
|
metadata = JSON.stringify({
|
|
1020
1023
|
data: { $hopCount }
|
package/src/ReturnSender.js
CHANGED
|
@@ -5,10 +5,11 @@
|
|
|
5
5
|
|
|
6
6
|
const ai = require('./Ai');
|
|
7
7
|
const { FEATURE_PHRASES, FEATURE_TRACKING } = require('./features');
|
|
8
|
-
const {
|
|
8
|
+
const { ResponseFlag } = require('./analytics/consts');
|
|
9
9
|
|
|
10
10
|
/** @typedef {import('./Request')} Request */
|
|
11
11
|
/** @typedef {import('./Responder')} Responder */
|
|
12
|
+
/** @typedef {import('./Processor').TrackingObject} TrackingObject */
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* @typedef {object} ChatLogStorage
|
|
@@ -155,6 +156,9 @@ class ReturnSender {
|
|
|
155
156
|
});
|
|
156
157
|
}
|
|
157
158
|
|
|
159
|
+
/**
|
|
160
|
+
* @returns {TrackingObject}
|
|
161
|
+
*/
|
|
158
162
|
get tracking () {
|
|
159
163
|
return this._tracking;
|
|
160
164
|
}
|
|
@@ -531,7 +535,7 @@ class ReturnSender {
|
|
|
531
535
|
};
|
|
532
536
|
}
|
|
533
537
|
|
|
534
|
-
if (!this._logger || meta.flag ===
|
|
538
|
+
if (!this._logger || meta.flag === ResponseFlag.DO_NOT_LOG) {
|
|
535
539
|
// noop
|
|
536
540
|
} else if (error) {
|
|
537
541
|
await Promise.resolve(this._logger
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author David Menger
|
|
3
|
+
*/
|
|
4
|
+
'use strict';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @enum {string}
|
|
8
|
+
*/
|
|
9
|
+
const ResponseFlag = {
|
|
10
|
+
/**
|
|
11
|
+
* Disambiguation quick reply was selected
|
|
12
|
+
*/
|
|
13
|
+
DISAMBIGUATION_SELECTED: 'd',
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Disambiguation occured - user was asked to choose the right meaning
|
|
17
|
+
*/
|
|
18
|
+
DISAMBIGUATION_OFFERED: 'o',
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Do not log the event
|
|
22
|
+
*/
|
|
23
|
+
DO_NOT_LOG: '!',
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Handover occurred
|
|
27
|
+
*/
|
|
28
|
+
HANDOVER: 'h'
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @enum {string}
|
|
33
|
+
*/
|
|
34
|
+
const TrackingType = { // max length 12
|
|
35
|
+
CONVERSATION_EVENT: 'conversation',
|
|
36
|
+
TRAINING: 'train',
|
|
37
|
+
PAGE_VIEW: 'page_view',
|
|
38
|
+
REPORT: 'report'
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @enum {string} TrackingCategory
|
|
43
|
+
*/
|
|
44
|
+
const TrackingCategory = { // max length 3
|
|
45
|
+
// PAGE_VIEW: 'page_view'
|
|
46
|
+
PAGE_VIEW_FIRST: 'pf',
|
|
47
|
+
PAGE_VIEW_SUBSEQUENT: 'pp',
|
|
48
|
+
|
|
49
|
+
// CONVERSATION_EVENT: 'conversation'
|
|
50
|
+
STICKER: 'sti',
|
|
51
|
+
IMAGE: 'img',
|
|
52
|
+
LOCATION: 'loc',
|
|
53
|
+
ATTACHMENT: 'att',
|
|
54
|
+
TEXT: 'txt',
|
|
55
|
+
QUICK_REPLY: 'qr',
|
|
56
|
+
OPT_IN: 'oin',
|
|
57
|
+
REFERRAL: 'ref',
|
|
58
|
+
POSTBACK_BUTTON: 'btn',
|
|
59
|
+
URL_LINK: 'url',
|
|
60
|
+
OTHER: 'oth',
|
|
61
|
+
HANDOVER_TO_BOT: 'bot',
|
|
62
|
+
|
|
63
|
+
// TRAINING: 'train'
|
|
64
|
+
INTENT_DETECTION: 'int',
|
|
65
|
+
DISAMBIGUATION_SELECTED: 'dis',
|
|
66
|
+
DISAMBIGUATION_OFFERED: 'dio',
|
|
67
|
+
|
|
68
|
+
// REPORT: 'report'
|
|
69
|
+
REPORT_FEEDBACK: 'fdb',
|
|
70
|
+
HANDOVER_OCCURRED: 'hum'
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* @type {Object<TrackingCategory,string>}
|
|
75
|
+
*/
|
|
76
|
+
const CATEGORY_LABELS = {
|
|
77
|
+
// PAGE_VIEW: 'page_view'
|
|
78
|
+
[TrackingCategory.PAGE_VIEW_FIRST]: 'Bot: Interaction',
|
|
79
|
+
[TrackingCategory.PAGE_VIEW_SUBSEQUENT]: 'Bot: Sub-interaction',
|
|
80
|
+
|
|
81
|
+
// CONVERSATION_EVENT: 'conversation'
|
|
82
|
+
[TrackingCategory.STICKER]: 'User: Sticker',
|
|
83
|
+
[TrackingCategory.IMAGE]: 'User: Image',
|
|
84
|
+
[TrackingCategory.LOCATION]: 'User: Location',
|
|
85
|
+
[TrackingCategory.ATTACHMENT]: 'User: Attachement', // yes, with typo
|
|
86
|
+
[TrackingCategory.TEXT]: 'User: Text',
|
|
87
|
+
[TrackingCategory.QUICK_REPLY]: 'User: Quick reply',
|
|
88
|
+
[TrackingCategory.OPT_IN]: 'Entry: Optin',
|
|
89
|
+
[TrackingCategory.REFERRAL]: 'Entry: Referral',
|
|
90
|
+
[TrackingCategory.POSTBACK_BUTTON]: 'User: Button - bot',
|
|
91
|
+
[TrackingCategory.URL_LINK]: 'User: Button - url',
|
|
92
|
+
[TrackingCategory.OTHER]: 'User: Other',
|
|
93
|
+
[TrackingCategory.HANDOVER_TO_BOT]: 'Entry: Handover in',
|
|
94
|
+
|
|
95
|
+
// TRAINING: 'train'
|
|
96
|
+
[TrackingCategory.INTENT_DETECTION]: 'Intent: Detection',
|
|
97
|
+
[TrackingCategory.DISAMBIGUATION_SELECTED]: 'Disambiguation: selected',
|
|
98
|
+
[TrackingCategory.DISAMBIGUATION_OFFERED]: 'Disambiguation: offered',
|
|
99
|
+
|
|
100
|
+
// REPORT: 'report'
|
|
101
|
+
[TrackingCategory.REPORT_FEEDBACK]: 'User: Feedback',
|
|
102
|
+
[TrackingCategory.HANDOVER_OCCURRED]: 'Bot: Handover out'
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
module.exports = {
|
|
106
|
+
CATEGORY_LABELS,
|
|
107
|
+
TrackingType,
|
|
108
|
+
TrackingCategory,
|
|
109
|
+
ResponseFlag
|
|
110
|
+
};
|
|
@@ -3,8 +3,11 @@
|
|
|
3
3
|
*/
|
|
4
4
|
'use strict';
|
|
5
5
|
|
|
6
|
-
const { replaceDiacritics } = require('webalize');
|
|
6
|
+
const { replaceDiacritics, webalize } = require('webalize');
|
|
7
7
|
const Ai = require('../Ai');
|
|
8
|
+
const {
|
|
9
|
+
TrackingType, TrackingCategory, CATEGORY_LABELS, ResponseFlag
|
|
10
|
+
} = require('./consts');
|
|
8
11
|
|
|
9
12
|
/** @typedef {import('../Processor').InteractionEvent} InteractionEvent */
|
|
10
13
|
/** @typedef {import('../Processor').IInteractionHandler} IInteractionHandler */
|
|
@@ -23,11 +26,61 @@ const Ai = require('../Ai');
|
|
|
23
26
|
|
|
24
27
|
/**
|
|
25
28
|
* @typedef {object} Event
|
|
26
|
-
* @prop {
|
|
27
|
-
* @prop {
|
|
29
|
+
* @prop {TrackingType} type
|
|
30
|
+
* @prop {TrackingCategory} [category]
|
|
28
31
|
* @prop {string} [action]
|
|
29
32
|
* @prop {string} [label]
|
|
30
33
|
* @prop {number} [value]
|
|
34
|
+
* @prop {string} [lang]
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @typedef {object} ConversationEventExtension
|
|
39
|
+
* @prop {string} [lastAction]
|
|
40
|
+
* @prop {string} [skill]
|
|
41
|
+
* @prop {string} [text]
|
|
42
|
+
* @prop {string} [expected]
|
|
43
|
+
* @prop {boolean} expectedTaken
|
|
44
|
+
* @prop {boolean} isContextUpdate
|
|
45
|
+
* @prop {boolean} isAttachment
|
|
46
|
+
* @prop {boolean} isNotification
|
|
47
|
+
* @prop {boolean} isQuickReply
|
|
48
|
+
* @prop {boolean} isPassThread
|
|
49
|
+
* @prop {boolean} isPostback
|
|
50
|
+
* @prop {boolean} isText
|
|
51
|
+
* @prop {boolean} didHandover
|
|
52
|
+
* @prop {boolean} withUser
|
|
53
|
+
* @prop {string} [userId]
|
|
54
|
+
* @prop {number} [feedback]
|
|
55
|
+
* @prop {string} [winnerAction]
|
|
56
|
+
* @prop {string} [winnerIntent]
|
|
57
|
+
* @prop {string[]|string} [winnerEntities]
|
|
58
|
+
* @prop {number} [winnerScore]
|
|
59
|
+
* @prop {boolean} [winnerTaken]
|
|
60
|
+
* @prop {string} [intent]
|
|
61
|
+
* @prop {number} [intentScore]
|
|
62
|
+
* @prop {string[]|string} [entities]
|
|
63
|
+
* @prop {string[]|string} allActions
|
|
64
|
+
* @prop {boolean} nonInteractive
|
|
65
|
+
*
|
|
66
|
+
* @typedef {Event & ConversationEventExtension} ConversationEvent
|
|
67
|
+
*/
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* @typedef {object} PageViewEventExtension
|
|
71
|
+
* @prop {string} [lastAction]
|
|
72
|
+
* @prop {string} [prevAction]
|
|
73
|
+
* @prop {string} [skill]
|
|
74
|
+
* @prop {string[]|string} allActions
|
|
75
|
+
* @prop {boolean} nonInteractive
|
|
76
|
+
* @prop {boolean} isGoto
|
|
77
|
+
* @prop {boolean} withUser
|
|
78
|
+
*
|
|
79
|
+
* @typedef {Event & PageViewEventExtension} PageViewEvent
|
|
80
|
+
*/
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* @typedef {ConversationEvent | Event | PageViewEvent} TrackingEvent
|
|
31
84
|
*/
|
|
32
85
|
|
|
33
86
|
/**
|
|
@@ -35,6 +88,13 @@ const Ai = require('../Ai');
|
|
|
35
88
|
* @prop {number} [sessionCount]
|
|
36
89
|
* @prop {string} [lang]
|
|
37
90
|
* @prop {string} [action]
|
|
91
|
+
* @prop {string} [snapshot]
|
|
92
|
+
* @prop {string} [botId]
|
|
93
|
+
* @prop {boolean} [didHandover]
|
|
94
|
+
* @prop {number|null} [feedback]
|
|
95
|
+
* @prop {string} [timeZone]
|
|
96
|
+
* @prop {number} [sessionStart]
|
|
97
|
+
* @prop {number} [sessionDuration]
|
|
38
98
|
*/
|
|
39
99
|
|
|
40
100
|
/**
|
|
@@ -53,11 +113,12 @@ const Ai = require('../Ai');
|
|
|
53
113
|
* @param {string} pageId
|
|
54
114
|
* @param {string} senderId
|
|
55
115
|
* @param {string} sessionId
|
|
56
|
-
* @param {
|
|
116
|
+
* @param {TrackingEvent[]} events
|
|
57
117
|
* @param {GAUser} [user]
|
|
58
118
|
* @param {number} [ts]
|
|
59
119
|
* @param {boolean} [nonInteractive]
|
|
60
120
|
* @param {boolean} [sessionStarted]
|
|
121
|
+
* @param {SessionMetadata} [metadata]
|
|
61
122
|
* @returns {Promise}
|
|
62
123
|
*/
|
|
63
124
|
|
|
@@ -72,6 +133,10 @@ const Ai = require('../Ai');
|
|
|
72
133
|
* @prop {StoreEvents} storeEvents
|
|
73
134
|
* @prop {CreateUserSession} createUserSession
|
|
74
135
|
* @prop {boolean} [hasExtendedEvents]
|
|
136
|
+
* @prop {boolean} [supportsArrays]
|
|
137
|
+
* @prop {boolean} [useDescriptiveCategories]
|
|
138
|
+
* @prop {boolean} [useExtendedScalars]
|
|
139
|
+
* @prop {boolean} [parallelSessionInsert]
|
|
75
140
|
*/
|
|
76
141
|
|
|
77
142
|
/**
|
|
@@ -88,7 +153,7 @@ const Ai = require('../Ai');
|
|
|
88
153
|
|
|
89
154
|
/**
|
|
90
155
|
* @typedef {object} TrackingEvents
|
|
91
|
-
* @prop {
|
|
156
|
+
* @prop {TrackingEvent[]} events
|
|
92
157
|
*/
|
|
93
158
|
|
|
94
159
|
/**
|
|
@@ -98,6 +163,9 @@ const Ai = require('../Ai');
|
|
|
98
163
|
|
|
99
164
|
/**
|
|
100
165
|
* @typedef {object} HandlerConfig
|
|
166
|
+
* @prop {string} [snapshot]
|
|
167
|
+
* @prop {string} [botId]
|
|
168
|
+
* @prop {string} [timeZone] - default UTC
|
|
101
169
|
* @prop {boolean} [enabled] - default true
|
|
102
170
|
* @prop {boolean} [throwException] - default false
|
|
103
171
|
* @prop {IGALogger} [log] - console like logger
|
|
@@ -134,12 +202,27 @@ function onInteractionHandler (
|
|
|
134
202
|
enabled = true,
|
|
135
203
|
throwException = false,
|
|
136
204
|
log = console,
|
|
205
|
+
snapshot,
|
|
206
|
+
botId,
|
|
207
|
+
timeZone = 'UTC',
|
|
137
208
|
anonymize = (x) => x,
|
|
138
209
|
userExtractor = (state) => null // eslint-disable-line no-unused-vars
|
|
139
210
|
},
|
|
140
211
|
analyticsStorage,
|
|
141
212
|
ai = Ai.ai
|
|
142
213
|
) {
|
|
214
|
+
const {
|
|
215
|
+
supportsArrays = false,
|
|
216
|
+
useExtendedScalars = false,
|
|
217
|
+
hasExtendedEvents = false,
|
|
218
|
+
useDescriptiveCategories = true,
|
|
219
|
+
parallelSessionInsert = false
|
|
220
|
+
} = analyticsStorage;
|
|
221
|
+
|
|
222
|
+
const asArray = (data = []) => (supportsArrays ? data : data.join(','));
|
|
223
|
+
const asCategory = (cat) => (useDescriptiveCategories && CATEGORY_LABELS[cat]) || cat;
|
|
224
|
+
const noneAction = useExtendedScalars ? null : '(none)';
|
|
225
|
+
const noneValue = useExtendedScalars ? null : 0;
|
|
143
226
|
|
|
144
227
|
/**
|
|
145
228
|
* @param {InteractionEvent} params
|
|
@@ -151,13 +234,14 @@ function onInteractionHandler (
|
|
|
151
234
|
// state,
|
|
152
235
|
// data,
|
|
153
236
|
skill,
|
|
154
|
-
|
|
237
|
+
events,
|
|
238
|
+
flag,
|
|
239
|
+
nonInteractive
|
|
155
240
|
}) {
|
|
156
241
|
if (!enabled) {
|
|
157
242
|
return;
|
|
158
243
|
}
|
|
159
244
|
try {
|
|
160
|
-
const nonInteractive = !!req.campaign;
|
|
161
245
|
const {
|
|
162
246
|
pageId,
|
|
163
247
|
senderId,
|
|
@@ -168,19 +252,52 @@ function onInteractionHandler (
|
|
|
168
252
|
_snew: createSession,
|
|
169
253
|
_sct: sessionCount,
|
|
170
254
|
_sid: sessionId,
|
|
255
|
+
_sst: sessionStart,
|
|
256
|
+
_sts: sessionTs,
|
|
171
257
|
lang
|
|
172
258
|
} = req.state;
|
|
173
259
|
|
|
174
|
-
const
|
|
260
|
+
const trackEvents = [];
|
|
261
|
+
|
|
262
|
+
const [action = noneAction, ...otherActions] = actions;
|
|
263
|
+
|
|
264
|
+
const feedbackEvent = events.find((e) => e.type === TrackingType.REPORT
|
|
265
|
+
&& e.category === TrackingCategory.REPORT_FEEDBACK);
|
|
266
|
+
const feedback = feedbackEvent
|
|
267
|
+
? feedbackEvent.value
|
|
268
|
+
: noneValue;
|
|
269
|
+
let didHandover = flag === ResponseFlag.HANDOVER;
|
|
270
|
+
const hasHandoverEvent = events
|
|
271
|
+
.some((e) => e.category === TrackingCategory.HANDOVER_OCCURRED);
|
|
272
|
+
|
|
273
|
+
if (didHandover && !hasHandoverEvent) {
|
|
274
|
+
trackEvents.push({
|
|
275
|
+
type: TrackingType.REPORT,
|
|
276
|
+
category: asCategory(TrackingCategory.HANDOVER_OCCURRED),
|
|
277
|
+
action: null,
|
|
278
|
+
label: null,
|
|
279
|
+
value: noneValue
|
|
280
|
+
});
|
|
281
|
+
} else if (hasHandoverEvent) {
|
|
282
|
+
didHandover = true;
|
|
283
|
+
}
|
|
175
284
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
285
|
+
const metadata = {
|
|
286
|
+
sessionCount,
|
|
287
|
+
lang,
|
|
288
|
+
action,
|
|
289
|
+
snapshot,
|
|
290
|
+
botId,
|
|
291
|
+
didHandover,
|
|
292
|
+
feedback,
|
|
293
|
+
timeZone,
|
|
294
|
+
sessionStart,
|
|
295
|
+
sessionDuration: sessionTs - sessionStart
|
|
296
|
+
};
|
|
182
297
|
|
|
183
|
-
|
|
298
|
+
let sessionPromise;
|
|
299
|
+
if (createSession) {
|
|
300
|
+
sessionPromise = analyticsStorage.createUserSession(
|
|
184
301
|
pageId,
|
|
185
302
|
senderId,
|
|
186
303
|
sessionId,
|
|
@@ -188,6 +305,11 @@ function onInteractionHandler (
|
|
|
188
305
|
timestamp,
|
|
189
306
|
nonInteractive
|
|
190
307
|
);
|
|
308
|
+
|
|
309
|
+
if (!parallelSessionInsert) {
|
|
310
|
+
await sessionPromise;
|
|
311
|
+
sessionPromise = null;
|
|
312
|
+
}
|
|
191
313
|
}
|
|
192
314
|
|
|
193
315
|
const [{
|
|
@@ -200,6 +322,7 @@ function onInteractionHandler (
|
|
|
200
322
|
: anonymize(
|
|
201
323
|
replaceDiacritics(req.text()).replace(/\s+/g, ' ').toLowerCase().trim()
|
|
202
324
|
);
|
|
325
|
+
const useSkill = (skill && webalize(skill)) || noneAction;
|
|
203
326
|
|
|
204
327
|
let winnerAction = '';
|
|
205
328
|
let winnerScore = 0;
|
|
@@ -211,7 +334,7 @@ function onInteractionHandler (
|
|
|
211
334
|
|
|
212
335
|
if (winners.length > 0) {
|
|
213
336
|
[{
|
|
214
|
-
action: winnerAction =
|
|
337
|
+
action: winnerAction = noneAction,
|
|
215
338
|
sort: winnerScore = 0,
|
|
216
339
|
intent: { intent: winnerIntent, entities: winnerEntities = [] }
|
|
217
340
|
}] = winners;
|
|
@@ -220,6 +343,7 @@ function onInteractionHandler (
|
|
|
220
343
|
}
|
|
221
344
|
|
|
222
345
|
const expected = req.expected() ? req.expected().action : '';
|
|
346
|
+
const user = userExtractor(req.state);
|
|
223
347
|
|
|
224
348
|
const isContextUpdate = req.isSetContext();
|
|
225
349
|
const isNotification = !!req.campaign;
|
|
@@ -229,13 +353,17 @@ function onInteractionHandler (
|
|
|
229
353
|
const isText = !isQuickReply && req.isText();
|
|
230
354
|
const isPostback = req.isPostBack();
|
|
231
355
|
|
|
232
|
-
const allActions = actions
|
|
356
|
+
const allActions = asArray(actions);
|
|
233
357
|
const requestAction = req.action();
|
|
234
358
|
|
|
235
|
-
const
|
|
359
|
+
const langsExtension = hasExtendedEvents
|
|
360
|
+
? { lang }
|
|
361
|
+
: { cd1: lang };
|
|
362
|
+
|
|
363
|
+
const withUser = user !== null && !!user.id;
|
|
236
364
|
|
|
237
365
|
const actionMeta = {
|
|
238
|
-
requestAction: req.action() ||
|
|
366
|
+
requestAction: req.action() || noneAction,
|
|
239
367
|
expected,
|
|
240
368
|
expectedTaken: requestAction === expected,
|
|
241
369
|
isContextUpdate,
|
|
@@ -245,47 +373,59 @@ function onInteractionHandler (
|
|
|
245
373
|
isPassThread,
|
|
246
374
|
isText,
|
|
247
375
|
isPostback,
|
|
376
|
+
didHandover,
|
|
377
|
+
withUser,
|
|
378
|
+
feedback,
|
|
379
|
+
skill: useSkill,
|
|
248
380
|
winnerAction,
|
|
249
381
|
winnerIntent,
|
|
250
|
-
winnerEntities: winnerEntities.map((e) => e.entity)
|
|
382
|
+
winnerEntities: asArray(winnerEntities.map((e) => e.entity)),
|
|
251
383
|
winnerScore,
|
|
252
384
|
winnerTaken,
|
|
253
385
|
intent,
|
|
254
386
|
intentScore: score,
|
|
255
|
-
entities: req.entities.map((e) => e.entity)
|
|
387
|
+
entities: asArray(req.entities.map((e) => e.entity)),
|
|
256
388
|
text,
|
|
257
389
|
allActions
|
|
258
390
|
};
|
|
259
391
|
|
|
260
|
-
|
|
261
|
-
|
|
392
|
+
const notHandled = actions.some((a) => a.match(/\*$/)) && !req.isQuickReply();
|
|
393
|
+
const value = notHandled ? 1 : 0;
|
|
394
|
+
|
|
395
|
+
trackEvents.push({
|
|
396
|
+
type: TrackingType.PAGE_VIEW,
|
|
397
|
+
category: asCategory(TrackingCategory.PAGE_VIEW_FIRST),
|
|
262
398
|
action,
|
|
399
|
+
label: (isText || isQuickReply ? text : null),
|
|
400
|
+
value,
|
|
263
401
|
allActions,
|
|
264
402
|
nonInteractive,
|
|
265
403
|
lastAction,
|
|
404
|
+
// @ts-ignore
|
|
266
405
|
prevAction: lastAction,
|
|
267
|
-
skill,
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
...
|
|
406
|
+
skill: useSkill,
|
|
407
|
+
isGoto: false,
|
|
408
|
+
withUser,
|
|
409
|
+
...langsExtension,
|
|
410
|
+
...(hasExtendedEvents ? {} : actionMeta)
|
|
271
411
|
});
|
|
272
412
|
|
|
273
413
|
let prevAction = action;
|
|
274
414
|
|
|
275
|
-
|
|
415
|
+
trackEvents.push(
|
|
276
416
|
...otherActions.map((a) => {
|
|
277
417
|
const r = {
|
|
278
|
-
type:
|
|
418
|
+
type: TrackingType.PAGE_VIEW,
|
|
419
|
+
category: asCategory(TrackingCategory.PAGE_VIEW_SUBSEQUENT),
|
|
279
420
|
action: a,
|
|
421
|
+
value: 0,
|
|
280
422
|
allActions,
|
|
281
423
|
nonInteractive: false,
|
|
282
424
|
lastAction,
|
|
283
425
|
prevAction,
|
|
284
|
-
skill,
|
|
426
|
+
skill: useSkill,
|
|
285
427
|
isGoto: true,
|
|
286
|
-
...
|
|
287
|
-
? { lang }
|
|
288
|
-
: { cd1: lang })
|
|
428
|
+
...langsExtension
|
|
289
429
|
};
|
|
290
430
|
|
|
291
431
|
prevAction = a;
|
|
@@ -293,104 +433,98 @@ function onInteractionHandler (
|
|
|
293
433
|
})
|
|
294
434
|
);
|
|
295
435
|
|
|
296
|
-
|
|
297
|
-
...
|
|
298
|
-
type, category, action: eventAction, label, value
|
|
436
|
+
trackEvents.push(
|
|
437
|
+
...events.map(({
|
|
438
|
+
type, category, action: eventAction, label, value: eVal
|
|
299
439
|
}) => ({
|
|
300
440
|
lastAction,
|
|
301
441
|
type,
|
|
302
|
-
category,
|
|
442
|
+
category: asCategory(category),
|
|
303
443
|
action: eventAction,
|
|
304
444
|
label,
|
|
305
|
-
value,
|
|
306
|
-
...
|
|
307
|
-
? { lang }
|
|
308
|
-
: { cd1: lang })
|
|
445
|
+
value: eVal,
|
|
446
|
+
...langsExtension
|
|
309
447
|
}))
|
|
310
448
|
);
|
|
311
449
|
|
|
312
450
|
if (!nonInteractive) {
|
|
313
451
|
|
|
314
452
|
if (req.isText()) {
|
|
315
|
-
|
|
316
|
-
type:
|
|
453
|
+
trackEvents.push({
|
|
454
|
+
type: TrackingType.TRAINING,
|
|
317
455
|
// @ts-ignore
|
|
318
456
|
lastAction,
|
|
319
|
-
category:
|
|
457
|
+
category: asCategory(TrackingCategory.INTENT_DETECTION),
|
|
320
458
|
intent,
|
|
321
459
|
action,
|
|
322
460
|
label: text,
|
|
323
461
|
value: score >= ai.confidence ? 0 : 1,
|
|
324
|
-
...
|
|
325
|
-
? { lang }
|
|
326
|
-
: { cd1: lang })
|
|
462
|
+
...langsExtension
|
|
327
463
|
});
|
|
328
464
|
}
|
|
329
465
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
let actionCategory = 'User: ';
|
|
333
|
-
let label = '(none)';
|
|
334
|
-
const value = notHandled ? 1 : 0;
|
|
466
|
+
let actionCategory;
|
|
467
|
+
let label = noneAction;
|
|
335
468
|
|
|
336
|
-
if (
|
|
337
|
-
actionCategory
|
|
469
|
+
if (isPassThread) {
|
|
470
|
+
actionCategory = TrackingCategory.HANDOVER_TO_BOT;
|
|
471
|
+
} else if (req.isSticker()) {
|
|
472
|
+
actionCategory = TrackingCategory.STICKER;
|
|
338
473
|
label = req.attachmentUrl(0);
|
|
339
474
|
} else if (req.isImage()) {
|
|
340
|
-
actionCategory
|
|
475
|
+
actionCategory = TrackingCategory.IMAGE;
|
|
341
476
|
label = req.attachmentUrl(0);
|
|
342
477
|
} else if (req.hasLocation()) {
|
|
343
|
-
actionCategory
|
|
478
|
+
actionCategory = TrackingCategory.LOCATION;
|
|
344
479
|
const { lat, long } = req.getLocation();
|
|
345
480
|
label = `${lat}, ${long}`;
|
|
346
481
|
} else if (isAttachment) {
|
|
347
|
-
actionCategory
|
|
482
|
+
actionCategory = TrackingCategory.ATTACHMENT;
|
|
348
483
|
label = req.attachment(0).type;
|
|
349
484
|
} else if (isText) {
|
|
350
|
-
actionCategory
|
|
485
|
+
actionCategory = TrackingCategory.TEXT;
|
|
351
486
|
label = text;
|
|
352
487
|
} else if (isQuickReply) {
|
|
353
|
-
actionCategory
|
|
488
|
+
actionCategory = TrackingCategory.QUICK_REPLY;
|
|
354
489
|
label = text;
|
|
355
|
-
} else if (req.
|
|
356
|
-
actionCategory =
|
|
357
|
-
|
|
358
|
-
|
|
490
|
+
} else if (req.isOptin()) {
|
|
491
|
+
actionCategory = TrackingCategory.OPT_IN;
|
|
492
|
+
} else if (req.isReferral()) {
|
|
493
|
+
actionCategory = TrackingCategory.REFERRAL;
|
|
359
494
|
} else if (isPostback) {
|
|
360
|
-
actionCategory
|
|
361
|
-
label = req.
|
|
495
|
+
actionCategory = TrackingCategory.POSTBACK_BUTTON;
|
|
496
|
+
label = req.event.postback.title || (useExtendedScalars ? null : '(unknown)');
|
|
362
497
|
} else {
|
|
363
|
-
actionCategory
|
|
498
|
+
actionCategory = TrackingCategory.OTHER;
|
|
364
499
|
}
|
|
365
500
|
|
|
366
|
-
|
|
501
|
+
trackEvents.push({
|
|
367
502
|
...(analyticsStorage.hasExtendedEvents ? actionMeta : {}),
|
|
368
|
-
type:
|
|
369
|
-
// @ts-ignore
|
|
503
|
+
type: TrackingType.CONVERSATION_EVENT,
|
|
370
504
|
lastAction,
|
|
371
|
-
category: actionCategory,
|
|
505
|
+
category: asCategory(actionCategory),
|
|
372
506
|
action,
|
|
373
507
|
label,
|
|
374
508
|
value,
|
|
375
|
-
...
|
|
376
|
-
? { lang }
|
|
377
|
-
: { cd1: lang })
|
|
509
|
+
...langsExtension
|
|
378
510
|
});
|
|
379
511
|
}
|
|
380
512
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
513
|
+
await Promise.all([
|
|
514
|
+
analyticsStorage.storeEvents(
|
|
515
|
+
pageId,
|
|
516
|
+
senderId,
|
|
517
|
+
sessionId,
|
|
518
|
+
// @ts-ignore
|
|
519
|
+
trackEvents,
|
|
520
|
+
user,
|
|
521
|
+
timestamp,
|
|
522
|
+
nonInteractive,
|
|
523
|
+
createSession,
|
|
524
|
+
metadata
|
|
525
|
+
),
|
|
526
|
+
sessionPromise
|
|
527
|
+
]);
|
|
394
528
|
} catch (e) {
|
|
395
529
|
if (throwException) {
|
|
396
530
|
throw e;
|
|
@@ -432,7 +566,7 @@ function onInteractionHandler (
|
|
|
432
566
|
[{
|
|
433
567
|
// @ts-ignore
|
|
434
568
|
lastAction,
|
|
435
|
-
...(
|
|
569
|
+
...(hasExtendedEvents
|
|
436
570
|
? { lang }
|
|
437
571
|
: { cd1: lang }),
|
|
438
572
|
...event
|
package/src/flags.js
CHANGED
|
@@ -18,8 +18,14 @@ const FLAG_DISAMBIGUATION_OFFERED = 'o';
|
|
|
18
18
|
*/
|
|
19
19
|
const FLAG_DO_NOT_LOG = '!';
|
|
20
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Handover occurred
|
|
23
|
+
*/
|
|
24
|
+
const FLAG_HANDOVER = 'h';
|
|
25
|
+
|
|
21
26
|
module.exports = {
|
|
22
27
|
FLAG_DISAMBIGUATION_SELECTED,
|
|
23
28
|
FLAG_DISAMBIGUATION_OFFERED,
|
|
24
|
-
FLAG_DO_NOT_LOG
|
|
29
|
+
FLAG_DO_NOT_LOG,
|
|
30
|
+
FLAG_HANDOVER
|
|
25
31
|
};
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
const { makeAbsolute } = require('./pathUtils');
|
|
7
7
|
const { tokenize } = require('./tokenizer');
|
|
8
|
-
const {
|
|
8
|
+
const { ResponseFlag } = require('../analytics/consts');
|
|
9
9
|
const { checkSetState } = require('./stateVariables');
|
|
10
10
|
|
|
11
11
|
/** @typedef {import('../Request')} Request */
|
|
@@ -240,7 +240,7 @@ function makeQuickReplies (replies, path = '', translate = (w) => w, quickReplyC
|
|
|
240
240
|
}
|
|
241
241
|
|
|
242
242
|
if (data._senderMeta
|
|
243
|
-
&& data._senderMeta.flag ===
|
|
243
|
+
&& data._senderMeta.flag === ResponseFlag.DISAMBIGUATION_SELECTED) {
|
|
244
244
|
|
|
245
245
|
const { likelyIntent } = data._senderMeta;
|
|
246
246
|
disambiguationIntents.push(likelyIntent);
|
|
@@ -411,7 +411,7 @@ function disambiguationQuickReply (title, likelyIntent, disambText, action, data
|
|
|
411
411
|
data: {
|
|
412
412
|
...data,
|
|
413
413
|
_senderMeta: {
|
|
414
|
-
flag:
|
|
414
|
+
flag: ResponseFlag.DISAMBIGUATION_SELECTED,
|
|
415
415
|
likelyIntent,
|
|
416
416
|
disambText
|
|
417
417
|
}
|
|
@@ -108,9 +108,12 @@ function checkSetState (setState, newState) {
|
|
|
108
108
|
* @returns {boolean}
|
|
109
109
|
*/
|
|
110
110
|
function isUserInteraction (req) {
|
|
111
|
-
return req.
|
|
112
|
-
|
|
113
|
-
|
|
111
|
+
return !req.campaign
|
|
112
|
+
&& !req.event.pass_thread_control
|
|
113
|
+
&& !req.isSetContext()
|
|
114
|
+
&& (req.isMessage() || req.isPostBack()
|
|
115
|
+
|| req.isReferral() || req.isAttachment()
|
|
116
|
+
|| req.isTextOrIntent());
|
|
114
117
|
}
|
|
115
118
|
|
|
116
119
|
/**
|