wingbot 3.46.0-alpha.10 → 3.46.0-alpha.11
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/package.json +1 -1
- package/src/Processor.js +2 -0
- package/src/ReturnSender.js +23 -2
- package/src/analytics/onInteractionHandler.js +4 -1
- 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/package.json
CHANGED
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,18 @@ 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
|
+
}
|
|
166
|
+
|
|
150
167
|
_gotAnotherEvent () {
|
|
151
168
|
if (this._gotAnotherEventDefer) {
|
|
152
169
|
this._gotAnotherEventDefer();
|
|
@@ -379,6 +396,10 @@ class ReturnSender {
|
|
|
379
396
|
return;
|
|
380
397
|
}
|
|
381
398
|
|
|
399
|
+
const text = extractText(payload);
|
|
400
|
+
if (text) {
|
|
401
|
+
this._responseTexts.push(text);
|
|
402
|
+
}
|
|
382
403
|
this._queue.push(payload);
|
|
383
404
|
this._gotAnotherEvent();
|
|
384
405
|
|
|
@@ -509,7 +530,7 @@ class ReturnSender {
|
|
|
509
530
|
async finished (req = null, res = null, err = null, reportError = console.error) {
|
|
510
531
|
this._finish(req);
|
|
511
532
|
const meta = this._createMeta(req, res);
|
|
512
|
-
|
|
533
|
+
this._confidentInput = !!req && req.isConfidentInput();
|
|
513
534
|
let error = err;
|
|
514
535
|
try {
|
|
515
536
|
await this._promise;
|
|
@@ -526,7 +547,7 @@ class ReturnSender {
|
|
|
526
547
|
const processedEvent = req
|
|
527
548
|
? req.event
|
|
528
549
|
: this._incommingMessage;
|
|
529
|
-
let incomming = this._filterMessage(processedEvent,
|
|
550
|
+
let incomming = this._filterMessage(processedEvent, this._confidentInput, req);
|
|
530
551
|
|
|
531
552
|
if (processedEvent !== this._incommingMessage) {
|
|
532
553
|
incomming = {
|
|
@@ -95,6 +95,7 @@ const {
|
|
|
95
95
|
* @prop {string} [timeZone]
|
|
96
96
|
* @prop {number} [sessionStart]
|
|
97
97
|
* @prop {number} [sessionDuration]
|
|
98
|
+
* @prop {string[]} [responseTexts]
|
|
98
99
|
*/
|
|
99
100
|
|
|
100
101
|
/**
|
|
@@ -236,7 +237,8 @@ function onInteractionHandler (
|
|
|
236
237
|
skill,
|
|
237
238
|
events,
|
|
238
239
|
flag,
|
|
239
|
-
nonInteractive
|
|
240
|
+
nonInteractive,
|
|
241
|
+
responseTexts
|
|
240
242
|
}) {
|
|
241
243
|
if (!enabled) {
|
|
242
244
|
return;
|
|
@@ -292,6 +294,7 @@ function onInteractionHandler (
|
|
|
292
294
|
feedback,
|
|
293
295
|
timeZone,
|
|
294
296
|
sessionStart,
|
|
297
|
+
responseTexts,
|
|
295
298
|
sessionDuration: sessionTs - sessionStart
|
|
296
299
|
};
|
|
297
300
|
|
|
@@ -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;
|