wingbot 3.67.29 → 3.67.31

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wingbot",
3
- "version": "3.67.29",
3
+ "version": "3.67.31",
4
4
  "description": "Enterprise Messaging Bot Conversation Engine",
5
5
  "main": "index.js",
6
6
  "type": "commonjs",
@@ -16,7 +16,7 @@
16
16
  },
17
17
  "repository": {
18
18
  "type": "git",
19
- "url": "git+ssh://git@github.com/wingbotai/wingbot.git"
19
+ "url": "git+ssh://git@github.com/wingbotai/wingnpbot.git"
20
20
  },
21
21
  "keywords": [
22
22
  "Enterprise",
package/src/Responder.js CHANGED
@@ -25,6 +25,8 @@ const EXCEPTION_HOPCOUNT_THRESHOLD = 5;
25
25
 
26
26
  /** @typedef {import('./Request')} Request */
27
27
  /** @typedef {import('./ReturnSender').UploadResult} UploadResult */
28
+ /** @typedef {import('./ReturnSender').SendOptions} SendOptions */
29
+ /** @typedef {import('./ReturnSender').TextFilter} TextFilter */
28
30
  /** @typedef {import('./analytics/consts').TrackingCategory} TrackingCategory */
29
31
  /** @typedef {import('./analytics/consts').TrackingType} TrackingType */
30
32
  /** @typedef {import('./transcript/transcriptFromHistory').Transcript} Transcript */
@@ -200,6 +202,9 @@ class Responder {
200
202
  this._textResponses = [];
201
203
 
202
204
  this._typingSent = false;
205
+
206
+ /** @type {SendOptions} */
207
+ this._nextMessageSendOptions = null;
203
208
  }
204
209
 
205
210
  _findPersonaConfiguration (name) {
@@ -369,7 +374,12 @@ class Responder {
369
374
  }
370
375
  this.startedOutput = true;
371
376
  this._typingSent = data.sender_action === 'typing_on';
372
- this._messageSender.send(data);
377
+ let opts;
378
+ if (!data.sender_action && this._nextMessageSendOptions) {
379
+ opts = this._nextMessageSendOptions;
380
+ this._nextMessageSendOptions = null;
381
+ }
382
+ this._messageSender.send(data, opts);
373
383
  return this;
374
384
  }
375
385
 
@@ -1311,6 +1321,17 @@ class Responder {
1311
1321
  );
1312
1322
  }
1313
1323
 
1324
+ /**
1325
+ * Set next message as confident
1326
+ *
1327
+ * @param {TextFilter} anonymizer
1328
+ */
1329
+ nextOutputConfident (anonymizer) {
1330
+ this._nextMessageSendOptions = {
1331
+ anonymizer
1332
+ };
1333
+ }
1334
+
1314
1335
  /**
1315
1336
  * Override action tracking
1316
1337
  *
@@ -32,9 +32,9 @@ const extractText = require('./transcript/extractText');
32
32
  /**
33
33
  * @typedef {object} ReturnSenderOptions
34
34
  * @prop {boolean} [dontWaitForDeferredOps]
35
- * @prop {textFilter} [textFilter] - filter for saving the texts
35
+ * @prop {TextFilter} [textFilter] - filter for saving the texts
36
36
  * @prop {boolean} [logStandbyEvents] - log the standby events
37
- * @prop {textFilter} [confidentInputFilter] - filter for confident input (@CONFIDENT)
37
+ * @prop {TextFilter} [confidentInputFilter] - filter for confident input (@CONFIDENT)
38
38
  */
39
39
 
40
40
  /**
@@ -53,11 +53,17 @@ const extractText = require('./transcript/extractText');
53
53
  * @prop {Function} error
54
54
  */
55
55
 
56
+ /**
57
+ * @typedef {object} SendOptions
58
+ * @prop {TextFilter} [anonymizer]
59
+ */
60
+
56
61
  /**
57
62
  * Text filter function
58
63
  *
59
- * @callback textFilter
64
+ * @callback TextFilter
60
65
  * @param {string} text - input text
66
+ * @param {'text'|'title'|'url'|'content'} key
61
67
  * @returns {string} - filtered text
62
68
  */
63
69
 
@@ -73,10 +79,11 @@ class ReturnSender {
73
79
  constructor (options, senderId, incommingMessage, logger = null) {
74
80
  this._queue = [];
75
81
 
76
- /**
77
- * @type {object[]}
78
- */
82
+ /** @type {object[]} */
79
83
  this.responses = [];
84
+
85
+ /** @type {SendOptions[]} */
86
+ this._responseOptions = [];
80
87
  this._results = [];
81
88
 
82
89
  this._promise = null;
@@ -131,7 +138,7 @@ class ReturnSender {
131
138
  * For example to remove any confidential data
132
139
  *
133
140
  * @param {string} text
134
- * @type {textFilter}
141
+ * @type {TextFilter}
135
142
  */
136
143
  this.textFilter = options.textFilter || ((text) => text);
137
144
 
@@ -227,7 +234,7 @@ class ReturnSender {
227
234
  : this.textFilter;
228
235
 
229
236
  return [
230
- filter(text).trim()
237
+ filter(text, null).trim()
231
238
  ];
232
239
  }
233
240
 
@@ -240,7 +247,7 @@ class ReturnSender {
240
247
  : this.textFilter;
241
248
 
242
249
  return this._responseTexts
243
- .map((t) => filter(t))
250
+ .map((t) => filter(t, null))
244
251
  .filter((t) => t && `${t}`.trim());
245
252
  }
246
253
 
@@ -300,11 +307,18 @@ class ReturnSender {
300
307
  return new Promise((r) => setTimeout(r, nextWait));
301
308
  }
302
309
 
303
- _filterMessage (payload, confidentInput = false, req = null) {
310
+ _filterMessage (payload, confidentInput = null, req = null) {
304
311
 
305
- const filter = confidentInput
306
- ? this.confidentInputFilter
307
- : this.textFilter;
312
+ let filter;
313
+ const processButtons = typeof confidentInput === 'function';
314
+
315
+ if (processButtons) {
316
+ filter = confidentInput;
317
+ } else if (confidentInput) {
318
+ filter = this.confidentInputFilter;
319
+ } else {
320
+ filter = this.textFilter;
321
+ }
308
322
 
309
323
  let { message } = payload;
310
324
 
@@ -314,7 +328,7 @@ class ReturnSender {
314
328
  text: message.text ? message.text : message.voice.ssml,
315
329
  voice: {
316
330
  ...message.voice,
317
- ssml: filter(message.voice.ssml)
331
+ ssml: filter(message.voice.ssml, 'ssml')
318
332
  }
319
333
  };
320
334
  }
@@ -331,7 +345,7 @@ class ReturnSender {
331
345
  ...payload,
332
346
  message: {
333
347
  ...message,
334
- text: filter(text)
348
+ text: filter(text, 'text')
335
349
  }
336
350
  };
337
351
  }
@@ -342,6 +356,8 @@ class ReturnSender {
342
356
  && message.attachment.payload
343
357
  && message.attachment.payload.text) {
344
358
 
359
+ const { payload: p } = message.attachment;
360
+
345
361
  return {
346
362
  ...payload,
347
363
  message: {
@@ -349,8 +365,46 @@ class ReturnSender {
349
365
  attachment: {
350
366
  ...message.attachment,
351
367
  payload: {
352
- ...message.attachment.payload,
353
- text: filter(message.attachment.payload.text)
368
+ ...p,
369
+ text: filter(p.text, 'text'),
370
+ ...(processButtons && Array.isArray(p.buttons)
371
+ ? {
372
+ buttons: p.buttons.map((btn) => {
373
+ switch (btn.type) {
374
+ case 'attachment':
375
+ return {
376
+ ...btn,
377
+ title: filter(btn.title, 'title'),
378
+ ...(btn.payload && btn.payload.content
379
+ ? {
380
+ payload: {
381
+ ...btn.payload,
382
+ content: filter(btn.payload.content, 'content')
383
+ }
384
+ }
385
+ : {}
386
+ )
387
+ };
388
+ case 'web_url':
389
+ return {
390
+ ...btn,
391
+ url: filter(btn.url, 'url'),
392
+ title: filter(btn.title, 'title')
393
+ };
394
+
395
+ case 'postback':
396
+ return {
397
+ ...btn,
398
+ title: filter(btn.title, 'title')
399
+ };
400
+
401
+ default:
402
+ return btn;
403
+ }
404
+ })
405
+ }
406
+ : {}
407
+ )
354
408
  }
355
409
  }
356
410
  }
@@ -363,10 +417,11 @@ class ReturnSender {
363
417
  async _work () {
364
418
  this._isWorking = true;
365
419
  let payload;
420
+ let options;
366
421
  let req;
367
422
  let previousResponse = null;
368
423
  while (this._queue.length > 0) {
369
- payload = this._queue.shift();
424
+ ({ payload, options } = this._queue.shift());
370
425
 
371
426
  let lastInQueueForNow = this._queue.length === 0;
372
427
  if (this._queue.length === 0 && this._sendLastMessageWithFinish) {
@@ -392,6 +447,7 @@ class ReturnSender {
392
447
  } else {
393
448
  await this._enrichPayload(payload, req, lastInQueueForNow);
394
449
  this.responses.push(payload);
450
+ this._responseOptions.push(options);
395
451
  previousResponse = await this._send(payload);
396
452
  this._results.push(previousResponse);
397
453
  }
@@ -487,7 +543,13 @@ class ReturnSender {
487
543
  return false;
488
544
  }
489
545
 
490
- send (payload) {
546
+ /**
547
+ *
548
+ * @param {object} payload
549
+ * @param {SendOptions} options
550
+ * @returns {void}
551
+ */
552
+ send (payload, options = {}) {
491
553
  if (this._finished) {
492
554
  throw new Error('Cannot send message after sender is finished');
493
555
  }
@@ -510,17 +572,17 @@ class ReturnSender {
510
572
  if (payload.set_context
511
573
  && !this._isVisibleMessage(payload, false)
512
574
  && lastInQueue
513
- && this._isVisibleMessage(lastInQueue)) {
575
+ && this._isVisibleMessage(lastInQueue.payload)) {
514
576
 
515
577
  const { set_context: setContext, ...rest } = payload;
516
578
 
517
579
  Object.assign(
518
- lastInQueue,
580
+ lastInQueue.payload,
519
581
  rest,
520
- lastInQueue,
582
+ lastInQueue.payload,
521
583
  {
522
584
  set_context: {
523
- ...lastInQueue.set_context,
585
+ ...lastInQueue.payload.set_context,
524
586
  ...setContext
525
587
  }
526
588
  }
@@ -532,7 +594,7 @@ class ReturnSender {
532
594
  if (text) {
533
595
  this._responseTexts.push(text);
534
596
  }
535
- this._queue.push(payload);
597
+ this._queue.push({ payload, options });
536
598
  this._gotAnotherEvent();
537
599
 
538
600
  if (this._catchedBeforeFinish) {
@@ -620,7 +682,7 @@ class ReturnSender {
620
682
  let text = req.text();
621
683
 
622
684
  if (text) {
623
- text = this.textFilter(text);
685
+ text = this.textFilter(text, null);
624
686
  }
625
687
 
626
688
  const expected = req.expected();
@@ -675,7 +737,10 @@ class ReturnSender {
675
737
  }
676
738
 
677
739
  try {
678
- const sent = this.responses.map((s) => this._filterMessage(s));
740
+ const sent = this.responses.map((s, i) => {
741
+ const opts = this._responseOptions[i];
742
+ return this._filterMessage(s, opts.anonymizer);
743
+ });
679
744
  const processedEvent = req
680
745
  ? req.event
681
746
  : this._incommingMessage;
@@ -4,7 +4,9 @@
4
4
  'use strict';
5
5
 
6
6
  const apiAuthorizer = require('./apiAuthorizer');
7
- const { apiTextOutputFilter } = require('../utils/deepMapTools');
7
+ const { apiTextOutputFilter, mapObject, defaultMapperFactory } = require('../utils/deepMapTools');
8
+
9
+ /** @typedef {import('../utils/deepMapTools').Mapper} Mapper */
8
10
 
9
11
  /**
10
12
  * @typedef {object} ConversationsAPI
@@ -31,7 +33,7 @@ const { apiTextOutputFilter } = require('../utils/deepMapTools');
31
33
  /**
32
34
  * Function for filtration of string output
33
35
  *
34
- * @callback textFilter
36
+ * @callback TextFilter
35
37
  * @param {string} value
36
38
  * @param {string} key
37
39
  * @returns {any}
@@ -46,7 +48,8 @@ const { apiTextOutputFilter } = require('../utils/deepMapTools');
46
48
  * @param {Notifications} notifications
47
49
  * @param {string[]|Function} [acl] - limit api to array of groups or use auth function
48
50
  * @param {object} options
49
- * @param {textFilter} [options.stateTextFilter] - optional funcion for filtering data in states
51
+ * @param {TextFilter} [options.stateTextFilter] - optional funcion for filtering data in states
52
+ * @param {Mapper} [options.mapper] - mapper for state values
50
53
  * @returns {ConversationsAPI}
51
54
  * @example
52
55
  * const { GraphApi, conversationsApi } = require('wingbot');
@@ -106,6 +109,13 @@ function conversationsApi (
106
109
  history,
107
110
  subscribtions
108
111
  });
112
+ } else if (options.mapper) {
113
+ mapState = (d) => mapObject({
114
+ ...d,
115
+ lastInteraction: d.lastInteraction || (new Date(0)),
116
+ history,
117
+ subscribtions
118
+ }, defaultMapperFactory(options.mapper));
109
119
  } else {
110
120
  mapState = (d) => ({
111
121
  ...d,
@@ -23,6 +23,58 @@ function deepEqual (left, right) {
23
23
  }
24
24
  }
25
25
 
26
+ /**
27
+ * @callback Mapper
28
+ * @param {string} key
29
+ * @param {any} val
30
+ * @param {object} lastObj
31
+ * @param {string|null} lastKey
32
+ * @returns {any}
33
+ */
34
+
35
+ /**
36
+ *
37
+ * @param {Mapper} [otherMapper]
38
+ * @returns {Mapper}
39
+ */
40
+ function defaultMapperFactory (otherMapper = null) {
41
+ return (k, v, o, lk) => {
42
+ const useV = otherMapper
43
+ ? otherMapper(k, v, o, lk)
44
+ : v;
45
+
46
+ if (useV instanceof Date) {
47
+ return v.toISOString();
48
+ }
49
+ return useV;
50
+ };
51
+ }
52
+
53
+ /**
54
+ *
55
+ * @template T
56
+ * @param {T} obj
57
+ * @param {Mapper} mapFn
58
+ * @returns {T}
59
+ */
60
+ function mapObject (obj, mapFn) {
61
+ let lastKey = null;
62
+ let lastObj = obj;
63
+
64
+ const str = JSON.stringify(obj, (key, val) => {
65
+ const ret = mapFn(key, val, lastObj, lastKey);
66
+
67
+ if (val && typeof val === 'object') {
68
+ lastObj = val;
69
+ lastKey = key || null;
70
+ }
71
+
72
+ return ret;
73
+ });
74
+
75
+ return JSON.parse(str);
76
+ }
77
+
26
78
  function apiTextOutputFilter (obj, callback, prevKey = '') {
27
79
  let useKey;
28
80
  if (Array.isArray(obj)) {
@@ -59,4 +111,6 @@ function apiTextOutputFilter (obj, callback, prevKey = '') {
59
111
  return obj;
60
112
  }
61
113
 
62
- module.exports = { apiTextOutputFilter, deepEqual };
114
+ module.exports = {
115
+ apiTextOutputFilter, deepEqual, mapObject, defaultMapperFactory
116
+ };