wingbot 3.46.0-alpha.9 → 3.46.1
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 +11 -1
- package/package.json +1 -1
- package/src/BotApp.js +40 -12
- package/src/Processor.js +2 -0
- package/src/ReturnSender.js +24 -2
- package/src/analytics/onInteractionHandler.js +7 -3
- package/src/tools/MemoryChatLogStorage.js +70 -0
- package/src/transcript/extractText.js +54 -0
- package/src/transcript/htmlBodyFromTranscript.js +21 -0
- package/src/transcript/textBodyFromTranscript.js +21 -0
- package/src/transcript/transcriptFromHistory.js +52 -0
package/index.js
CHANGED
|
@@ -48,6 +48,10 @@ const compileWithState = require('./src/utils/compileWithState');
|
|
|
48
48
|
const onInteractionHandler = require('./src/analytics/onInteractionHandler');
|
|
49
49
|
const GA4 = require('./src/analytics/GA4');
|
|
50
50
|
const plugins = require('./plugins/plugins.json');
|
|
51
|
+
const extractText = require('./src/transcript/extractText');
|
|
52
|
+
const htmlBodyFromTranscript = require('./src/transcript/htmlBodyFromTranscript');
|
|
53
|
+
const textBodyFromTranscript = require('./src/transcript/textBodyFromTranscript');
|
|
54
|
+
const transcriptFromHistory = require('./src/transcript/transcriptFromHistory');
|
|
51
55
|
const {
|
|
52
56
|
bufferloader,
|
|
53
57
|
MemoryStateStorage
|
|
@@ -135,5 +139,11 @@ module.exports = {
|
|
|
135
139
|
|
|
136
140
|
// ANALYTICS
|
|
137
141
|
onInteractionHandler,
|
|
138
|
-
GA4
|
|
142
|
+
GA4,
|
|
143
|
+
|
|
144
|
+
// TRANSCRIPTS
|
|
145
|
+
extractText,
|
|
146
|
+
htmlBodyFromTranscript,
|
|
147
|
+
textBodyFromTranscript,
|
|
148
|
+
transcriptFromHistory
|
|
139
149
|
};
|
package/package.json
CHANGED
package/src/BotApp.js
CHANGED
|
@@ -104,6 +104,14 @@ class BotApp {
|
|
|
104
104
|
this._logger = options.log || console;
|
|
105
105
|
this._textFilter = options.textFilter;
|
|
106
106
|
|
|
107
|
+
this._preHeatCalled = false;
|
|
108
|
+
this._preHeat = [
|
|
109
|
+
this._logger,
|
|
110
|
+
chatLogStorage,
|
|
111
|
+
processorOptions.stateStorage,
|
|
112
|
+
processorOptions.tokenStorage
|
|
113
|
+
].filter((s) => s && typeof s.preHeat === 'function');
|
|
114
|
+
|
|
107
115
|
let { apiUrl } = options;
|
|
108
116
|
|
|
109
117
|
if (!apiUrl) apiUrl = DEFAULT_API_URL;
|
|
@@ -204,6 +212,10 @@ class BotApp {
|
|
|
204
212
|
analyticsStorage.setDefaultLogger(log);
|
|
205
213
|
}
|
|
206
214
|
|
|
215
|
+
if (typeof analyticsStorage.preHeat === 'function') {
|
|
216
|
+
this._preHeat.push(analyticsStorage);
|
|
217
|
+
}
|
|
218
|
+
|
|
207
219
|
// @ts-ignore
|
|
208
220
|
const { snapshot = null, botId = null } = this._bot;
|
|
209
221
|
|
|
@@ -440,6 +452,15 @@ class BotApp {
|
|
|
440
452
|
return this._errorResponse('Missing authentication header', 401);
|
|
441
453
|
}
|
|
442
454
|
|
|
455
|
+
let preHeatPromise = null;
|
|
456
|
+
if (!this._preHeatCalled) {
|
|
457
|
+
preHeatPromise = Promise.all(
|
|
458
|
+
this._preHeat.map((p) => Promise.resolve(p.preHeat())
|
|
459
|
+
.catch((e) => this._logger.error('BotApp: preHeat failed', e)))
|
|
460
|
+
);
|
|
461
|
+
this._preHeatCalled = true;
|
|
462
|
+
}
|
|
463
|
+
|
|
443
464
|
let sha1;
|
|
444
465
|
let appId;
|
|
445
466
|
let secret;
|
|
@@ -449,6 +470,7 @@ class BotApp {
|
|
|
449
470
|
// @ts-ignore
|
|
450
471
|
({ sha1, appId } = await this._verify(token, secret));
|
|
451
472
|
} catch (e) {
|
|
473
|
+
await Promise.resolve(preHeatPromise);
|
|
452
474
|
return this._errorResponse(`Failed to verify token: ${e.message}`, 403);
|
|
453
475
|
}
|
|
454
476
|
|
|
@@ -457,22 +479,28 @@ class BotApp {
|
|
|
457
479
|
.digest('hex');
|
|
458
480
|
|
|
459
481
|
if (sha1 !== bodySha1) {
|
|
482
|
+
await Promise.resolve(preHeatPromise);
|
|
460
483
|
return this._errorResponse(`SHA1 does not match. Got in token: '${sha1}'`, 403);
|
|
461
484
|
}
|
|
462
485
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
const entry = await this._processEntries(appId, secret, body.entry, rawHeaders);
|
|
486
|
+
try {
|
|
487
|
+
const body = JSON.parse(rawBody);
|
|
466
488
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
489
|
+
const entry = await this._processEntries(appId, secret, body.entry, rawHeaders);
|
|
490
|
+
await Promise.resolve(preHeatPromise);
|
|
491
|
+
return {
|
|
492
|
+
statusCode: 200,
|
|
493
|
+
body: JSON.stringify({
|
|
494
|
+
entry
|
|
495
|
+
}),
|
|
496
|
+
headers: {
|
|
497
|
+
'Content-Type': 'application/json'
|
|
498
|
+
}
|
|
499
|
+
};
|
|
500
|
+
} catch (e) {
|
|
501
|
+
await Promise.resolve(preHeatPromise);
|
|
502
|
+
throw e;
|
|
503
|
+
}
|
|
476
504
|
}
|
|
477
505
|
|
|
478
506
|
async _processEntries (appId, secret, entry = [], headers = {}) {
|
package/src/Processor.js
CHANGED
|
@@ -62,6 +62,7 @@ const { mergeState, isUserInteraction } = require('./utils/stateVariables');
|
|
|
62
62
|
* @prop {TrackingEvent[]} events
|
|
63
63
|
* @prop {ResponseFlag|null} flag
|
|
64
64
|
* @prop {boolean} nonInteractive
|
|
65
|
+
* @prop {string[]} responseTexts
|
|
65
66
|
*/
|
|
66
67
|
|
|
67
68
|
/**
|
|
@@ -434,6 +435,7 @@ class Processor extends EventEmitter {
|
|
|
434
435
|
const { events = [] } = messageSender.tracking;
|
|
435
436
|
|
|
436
437
|
const event = {
|
|
438
|
+
responseTexts: messageSender.responseTexts,
|
|
437
439
|
req,
|
|
438
440
|
actions,
|
|
439
441
|
lastAction,
|
package/src/ReturnSender.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
const ai = require('./Ai');
|
|
7
7
|
const { FEATURE_PHRASES, FEATURE_TRACKING } = require('./features');
|
|
8
8
|
const { ResponseFlag } = require('./analytics/consts');
|
|
9
|
+
const extractText = require('./transcript/extractText');
|
|
9
10
|
|
|
10
11
|
/** @typedef {import('./Request')} Request */
|
|
11
12
|
/** @typedef {import('./Responder')} Responder */
|
|
@@ -122,8 +123,12 @@ class ReturnSender {
|
|
|
122
123
|
events: []
|
|
123
124
|
};
|
|
124
125
|
|
|
126
|
+
this._responseTexts = [];
|
|
127
|
+
|
|
125
128
|
this._intentsAndEntities = [];
|
|
126
129
|
|
|
130
|
+
this._confidentInput = false;
|
|
131
|
+
|
|
127
132
|
/**
|
|
128
133
|
* @type {Function}
|
|
129
134
|
* @private
|
|
@@ -147,6 +152,19 @@ class ReturnSender {
|
|
|
147
152
|
return this._simulatesOptIn;
|
|
148
153
|
}
|
|
149
154
|
|
|
155
|
+
/**
|
|
156
|
+
* @returns {string[]}
|
|
157
|
+
*/
|
|
158
|
+
get responseTexts () {
|
|
159
|
+
const filter = this._confidentInput
|
|
160
|
+
? this.confidentInputFilter
|
|
161
|
+
: this.textFilter;
|
|
162
|
+
|
|
163
|
+
return this._responseTexts
|
|
164
|
+
.map((t) => filter(t))
|
|
165
|
+
.filter((t) => t && `${t}`.trim());
|
|
166
|
+
}
|
|
167
|
+
|
|
150
168
|
_gotAnotherEvent () {
|
|
151
169
|
if (this._gotAnotherEventDefer) {
|
|
152
170
|
this._gotAnotherEventDefer();
|
|
@@ -379,6 +397,10 @@ class ReturnSender {
|
|
|
379
397
|
return;
|
|
380
398
|
}
|
|
381
399
|
|
|
400
|
+
const text = extractText(payload);
|
|
401
|
+
if (text) {
|
|
402
|
+
this._responseTexts.push(text);
|
|
403
|
+
}
|
|
382
404
|
this._queue.push(payload);
|
|
383
405
|
this._gotAnotherEvent();
|
|
384
406
|
|
|
@@ -509,7 +531,7 @@ class ReturnSender {
|
|
|
509
531
|
async finished (req = null, res = null, err = null, reportError = console.error) {
|
|
510
532
|
this._finish(req);
|
|
511
533
|
const meta = this._createMeta(req, res);
|
|
512
|
-
|
|
534
|
+
this._confidentInput = !!req && req.isConfidentInput();
|
|
513
535
|
let error = err;
|
|
514
536
|
try {
|
|
515
537
|
await this._promise;
|
|
@@ -526,7 +548,7 @@ class ReturnSender {
|
|
|
526
548
|
const processedEvent = req
|
|
527
549
|
? req.event
|
|
528
550
|
: this._incommingMessage;
|
|
529
|
-
let incomming = this._filterMessage(processedEvent,
|
|
551
|
+
let incomming = this._filterMessage(processedEvent, this._confidentInput, req);
|
|
530
552
|
|
|
531
553
|
if (processedEvent !== this._incommingMessage) {
|
|
532
554
|
incomming = {
|
|
@@ -75,8 +75,6 @@ const {
|
|
|
75
75
|
* @prop {boolean} nonInteractive
|
|
76
76
|
* @prop {boolean} isGoto
|
|
77
77
|
* @prop {boolean} withUser
|
|
78
|
-
* @prop {string} [snapshot]
|
|
79
|
-
* @prop {string} [botId]
|
|
80
78
|
*
|
|
81
79
|
* @typedef {Event & PageViewEventExtension} PageViewEvent
|
|
82
80
|
*/
|
|
@@ -96,6 +94,8 @@ const {
|
|
|
96
94
|
* @prop {number|null} [feedback]
|
|
97
95
|
* @prop {string} [timeZone]
|
|
98
96
|
* @prop {number} [sessionStart]
|
|
97
|
+
* @prop {number} [sessionDuration]
|
|
98
|
+
* @prop {string[]} [responseTexts]
|
|
99
99
|
*/
|
|
100
100
|
|
|
101
101
|
/**
|
|
@@ -138,6 +138,7 @@ const {
|
|
|
138
138
|
* @prop {boolean} [useDescriptiveCategories]
|
|
139
139
|
* @prop {boolean} [useExtendedScalars]
|
|
140
140
|
* @prop {boolean} [parallelSessionInsert]
|
|
141
|
+
* @prop {Function} [preHeat]
|
|
141
142
|
*/
|
|
142
143
|
|
|
143
144
|
/**
|
|
@@ -237,7 +238,8 @@ function onInteractionHandler (
|
|
|
237
238
|
skill,
|
|
238
239
|
events,
|
|
239
240
|
flag,
|
|
240
|
-
nonInteractive
|
|
241
|
+
nonInteractive,
|
|
242
|
+
responseTexts
|
|
241
243
|
}) {
|
|
242
244
|
if (!enabled) {
|
|
243
245
|
return;
|
|
@@ -293,6 +295,7 @@ function onInteractionHandler (
|
|
|
293
295
|
feedback,
|
|
294
296
|
timeZone,
|
|
295
297
|
sessionStart,
|
|
298
|
+
responseTexts,
|
|
296
299
|
sessionDuration: sessionTs - sessionStart
|
|
297
300
|
};
|
|
298
301
|
|
|
@@ -426,6 +429,7 @@ function onInteractionHandler (
|
|
|
426
429
|
prevAction,
|
|
427
430
|
skill: useSkill,
|
|
428
431
|
isGoto: true,
|
|
432
|
+
withUser,
|
|
429
433
|
...langsExtension
|
|
430
434
|
};
|
|
431
435
|
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author David Menger
|
|
3
|
+
*/
|
|
4
|
+
'use strict';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @class MemoryChatLogStorage
|
|
8
|
+
*/
|
|
9
|
+
class MemoryChatLogStorage {
|
|
10
|
+
|
|
11
|
+
constructor () {
|
|
12
|
+
this._logs = new Map();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Interate history
|
|
17
|
+
* all limits are inclusive
|
|
18
|
+
*
|
|
19
|
+
* @param {string} senderId
|
|
20
|
+
* @param {string} pageId
|
|
21
|
+
* @param {number} [limit]
|
|
22
|
+
* @param {number} [endAt] - iterate backwards to history
|
|
23
|
+
* @param {number} [startAt] - iterate forward to last interaction
|
|
24
|
+
* @returns {Promise<object[]>}
|
|
25
|
+
*/
|
|
26
|
+
async getInteractions (senderId, pageId, limit = 10, endAt = null, startAt = null) { // eslint-disable-line max-len,no-unused-vars
|
|
27
|
+
const events = this._getEvents(senderId);
|
|
28
|
+
return events.slice(-limit);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
*
|
|
33
|
+
* @param {string} senderId
|
|
34
|
+
* @returns {object[]}
|
|
35
|
+
*/
|
|
36
|
+
_getEvents (senderId) {
|
|
37
|
+
let events = this._logs.get(senderId);
|
|
38
|
+
if (!events) {
|
|
39
|
+
events = [];
|
|
40
|
+
this._logs.set(senderId, events);
|
|
41
|
+
}
|
|
42
|
+
return events;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Log single event
|
|
47
|
+
*
|
|
48
|
+
* @param {string} senderId
|
|
49
|
+
* @param {object[]} responses - list of sent responses
|
|
50
|
+
* @param {object} request - event request
|
|
51
|
+
* @param {object} [metadata] - request metadata
|
|
52
|
+
* @returns {void}
|
|
53
|
+
*/
|
|
54
|
+
log (senderId, responses = [], request = {}, metadata = {}) {
|
|
55
|
+
const events = this._getEvents(senderId);
|
|
56
|
+
events.push({
|
|
57
|
+
senderId,
|
|
58
|
+
request,
|
|
59
|
+
responses,
|
|
60
|
+
...metadata
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
error (error, senderId, sent, incomming, meta) {
|
|
65
|
+
return this.log(senderId, sent, incomming, { ...meta, error });
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
module.exports = MemoryChatLogStorage;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author David Menger
|
|
3
|
+
*/
|
|
4
|
+
'use strict';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Extracts text from conversational event
|
|
8
|
+
*
|
|
9
|
+
* @param {object} payload
|
|
10
|
+
* @returns {string|null}
|
|
11
|
+
*/
|
|
12
|
+
function extractText (payload) {
|
|
13
|
+
|
|
14
|
+
// text message
|
|
15
|
+
if (payload.message && payload.message.text) {
|
|
16
|
+
return payload.message.text;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// button message
|
|
20
|
+
if (payload.message && payload.message.attachment
|
|
21
|
+
&& payload.message.attachment.type === 'template'
|
|
22
|
+
&& payload.message.attachment.payload
|
|
23
|
+
&& payload.message.attachment.payload.text) {
|
|
24
|
+
|
|
25
|
+
return payload.message.attachment.payload.text;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (!payload.postback) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// postback with title
|
|
33
|
+
if (payload.postback.title) {
|
|
34
|
+
return payload.postback.title;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (payload.postback.payload && payload.postback.payload.action) {
|
|
38
|
+
return payload.postback.payload.action;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (typeof payload.postback.payload !== 'string') {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (payload.postback.payload[0] === '{') {
|
|
46
|
+
const pl = JSON.parse(payload.postback.payload);
|
|
47
|
+
|
|
48
|
+
return pl.action;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return payload.postback.payload;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
module.exports = extractText;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author David Menger
|
|
3
|
+
*/
|
|
4
|
+
'use strict';
|
|
5
|
+
|
|
6
|
+
/** @typedef {import('./transcriptFromHistory').Transcript} Transcript */
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @param {Transcript[]} transcript
|
|
10
|
+
* @param {string} [userSide]
|
|
11
|
+
* @param {string} [botSide]
|
|
12
|
+
* @returns {string}
|
|
13
|
+
*/
|
|
14
|
+
function htmlBodyFromTranscript (transcript, userSide = 'User', botSide = 'Bot') {
|
|
15
|
+
|
|
16
|
+
return transcript
|
|
17
|
+
.map((msg) => `<b>${msg.fromBot ? botSide : userSide}:</b> ${msg.text}`)
|
|
18
|
+
.join('<br />');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
module.exports = htmlBodyFromTranscript;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author David Menger
|
|
3
|
+
*/
|
|
4
|
+
'use strict';
|
|
5
|
+
|
|
6
|
+
/** @typedef {import('./transcriptFromHistory').Transcript} Transcript */
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @param {Transcript[]} transcript
|
|
10
|
+
* @param {string} [userSide]
|
|
11
|
+
* @param {string} [botSide]
|
|
12
|
+
* @returns {string}
|
|
13
|
+
*/
|
|
14
|
+
function textBodyFromTranscript (transcript, userSide = 'User', botSide = 'Bot') {
|
|
15
|
+
|
|
16
|
+
return transcript
|
|
17
|
+
.map((msg, i) => `${msg.fromBot ? ' <' : `${i > 0 ? '\n' : ''}# >`} ${msg.fromBot ? botSide : userSide}: ${msg.text}`)
|
|
18
|
+
.join('\n');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
module.exports = textBodyFromTranscript;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author David Menger
|
|
3
|
+
*/
|
|
4
|
+
'use strict';
|
|
5
|
+
|
|
6
|
+
const extractText = require('./extractText');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @callback GetInteractions
|
|
10
|
+
* @param {string} senderId
|
|
11
|
+
* @param {string} pageId
|
|
12
|
+
* @param {number} limit
|
|
13
|
+
* @returns {Promise<object[]>}
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @typedef {object} IChatStorage
|
|
18
|
+
* @prop {GetInteractions} getInteractions
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @typedef {object} Transcript
|
|
23
|
+
* @prop {string} text
|
|
24
|
+
* @prop {boolean} fromBot
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
*
|
|
29
|
+
* @param {IChatStorage} chatLogStorage
|
|
30
|
+
* @param {string} senderId
|
|
31
|
+
* @param {string} pageId
|
|
32
|
+
* @param {number} limit
|
|
33
|
+
* @returns {Promise<Transcript[]>}
|
|
34
|
+
*/
|
|
35
|
+
async function transcriptFromHistory (chatLogStorage, senderId, pageId, limit = 20) {
|
|
36
|
+
const data = await chatLogStorage.getInteractions(senderId, pageId, limit);
|
|
37
|
+
|
|
38
|
+
return data
|
|
39
|
+
.map((turn) => {
|
|
40
|
+
const { request, responses = [] } = turn;
|
|
41
|
+
|
|
42
|
+
return [
|
|
43
|
+
{ fromBot: false, text: extractText(request) },
|
|
44
|
+
...responses
|
|
45
|
+
.map((response) => ({ fromBot: true, text: extractText(response) }))
|
|
46
|
+
];
|
|
47
|
+
})
|
|
48
|
+
.reduce((ret, arr) => [...ret, ...arr], [])
|
|
49
|
+
.filter((ret) => !!ret.text);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
module.exports = transcriptFromHistory;
|