wingbot 3.43.0 → 3.44.0-alpha.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wingbot",
3
- "version": "3.43.0",
3
+ "version": "3.44.0-alpha.1",
4
4
  "description": "Enterprise Messaging Bot Conversation Engine",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/src/Ai.js CHANGED
@@ -49,6 +49,8 @@ let uq = 1;
49
49
  /** @typedef {import('./wingbot/CustomEntityDetectionModel').EntityDetector} EntityDetector */
50
50
  /** @typedef {import('./wingbot/CustomEntityDetectionModel').DetectorOptions} DetectorOptions */
51
51
 
52
+ /** @typedef {[string,EntityDetector|RegExp,DetectorOptions]} DetectorArgs */
53
+
52
54
  /**
53
55
  * @class Ai
54
56
  */
@@ -63,7 +65,7 @@ class Ai {
63
65
 
64
66
  /**
65
67
  * @private
66
- * @type {Map<string,[string,EntityDetector|RegExp,DetectorOptions]>}
68
+ * @type {Map<string,DetectorArgs>}
67
69
  */
68
70
  this._detectors = new Map(
69
71
  systemEntities.map((a) => [a[0], a])
package/src/Processor.js CHANGED
@@ -4,12 +4,13 @@
4
4
  'use strict';
5
5
 
6
6
  const EventEmitter = require('events');
7
+ const crypto = require('crypto');
7
8
  const { MemoryStateStorage } = require('./tools');
8
9
  const Responder = require('./Responder');
9
10
  const Request = require('./Request');
10
11
  const Ai = require('./Ai');
11
12
  const ReturnSender = require('./ReturnSender');
12
- const { mergeState } = require('./utils/stateVariables');
13
+ const { mergeState, isUserInteraction } = require('./utils/stateVariables');
13
14
 
14
15
  /** @typedef {import('./wingbot/CustomEntityDetectionModel').Intent} Intent */
15
16
  /** @typedef {import('./ReducerWrapper')} ReducerWrapper */
@@ -84,6 +85,7 @@ const { mergeState } = require('./utils/stateVariables');
84
85
  * @prop {string} [secret] - Secret for calling orchestrator API
85
86
  * @prop {string} [apiUrl] - Url for calling orchestrator API
86
87
  * @prop {Function} [fetch] - Fetch function for calling orchestrator API
88
+ * @prop {number} [sessionDuration] - Session duration for analytic purposes
87
89
  */
88
90
 
89
91
  /**
@@ -111,6 +113,8 @@ function NAME_FROM_STATE (state) {
111
113
  return null;
112
114
  }
113
115
 
116
+ const MAX_TS = 9999999999999;
117
+
114
118
  /**
115
119
  * Messaging event processor
116
120
  *
@@ -144,7 +148,8 @@ class Processor extends EventEmitter {
144
148
  autoTyping: false,
145
149
  autoSeen: false,
146
150
  redirectLimit: 20,
147
- nameFromState: NAME_FROM_STATE
151
+ nameFromState: NAME_FROM_STATE,
152
+ sessionDuration: 1800000 // 30 minutes
148
153
  };
149
154
 
150
155
  Object.assign(this.options, options);
@@ -483,9 +488,10 @@ class Processor extends EventEmitter {
483
488
 
484
489
  try {
485
490
  // ensure the request was not processed
491
+ const timestamp = message.timestamp || Date.now();
486
492
  if (fromEvent
487
493
  && stateObject.lastTimestamps && message.timestamp
488
- && stateObject.lastTimestamps.indexOf(message.timestamp) !== -1) {
494
+ && stateObject.lastTimestamps.indexOf(timestamp) !== -1) {
489
495
  throw Object.assign(new Error('Message has been already processed'), { code: 204 });
490
496
  }
491
497
 
@@ -523,6 +529,40 @@ class Processor extends EventEmitter {
523
529
  configuration
524
530
  );
525
531
 
532
+ // process session
533
+ if (fromEvent) {
534
+ let {
535
+ _sct: sessionCount = 0,
536
+ _sid: sessionId = null,
537
+ _segStamp: ts = 0,
538
+ _snew: sessionCreated
539
+ } = state;
540
+
541
+ if ((isUserInteraction(req)
542
+ && (ts + this.options.sessionDuration) < Date.now())
543
+ || !sessionId) {
544
+
545
+ sessionId = Processor._createSessionId(req.pageId, req.senderId, timestamp);
546
+ sessionCount++;
547
+ sessionCreated = true;
548
+ } else {
549
+ sessionCreated = false;
550
+ }
551
+
552
+ ts = timestamp;
553
+
554
+ Object.assign(state, {
555
+ _sct: sessionCount,
556
+ _sid: sessionId,
557
+ _segStamp: ts,
558
+ _snew: sessionCreated
559
+ });
560
+ } else {
561
+ Object.assign(state, {
562
+ _snew: false
563
+ });
564
+ }
565
+
526
566
  const features = [
527
567
  ...(this.options.features || []),
528
568
  ...req.features
@@ -650,7 +690,7 @@ class Processor extends EventEmitter {
650
690
  let lastTimestamps = stateObject.lastTimestamps || [];
651
691
  if (message.timestamp) {
652
692
  lastTimestamps = lastTimestamps.slice(-9);
653
- lastTimestamps.push(message.timestamp);
693
+ lastTimestamps.push(timestamp);
654
694
  }
655
695
 
656
696
  Object.assign(stateObject, {
@@ -698,6 +738,27 @@ class Processor extends EventEmitter {
698
738
  }
699
739
  }
700
740
 
741
+ static _createSessionId (pageId, senderId, timestamp = Date.now()) {
742
+ const senderHash = crypto.createHash('shake256', { outputLength: 6 })
743
+ .update(`${senderId}|${pageId}`)
744
+ .digest('hex');
745
+
746
+ const senderShort = parseInt(senderHash, 16).toString(36);
747
+
748
+ const rand = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER)
749
+ .toString(36);
750
+
751
+ const randTS = Math.floor(Date.now() % 1000)
752
+ .toString(36);
753
+
754
+ const ts = Math.floor(MAX_TS - timestamp)
755
+ .toString(36);
756
+
757
+ return `${ts}.${senderShort}`
758
+ .padEnd(21, randTS)
759
+ .padEnd(28, rand);
760
+ }
761
+
701
762
  /**
702
763
  *
703
764
  * @private
@@ -840,4 +901,6 @@ class Processor extends EventEmitter {
840
901
 
841
902
  }
842
903
 
904
+ Processor._createSessionId('p', 's');
905
+
843
906
  module.exports = Processor;
@@ -40,6 +40,13 @@ function getVoiceControlFromParams (params, lang = null) {
40
40
  }
41
41
  });
42
42
 
43
+ // remove empty values ("")
44
+ Object.keys(voiceControl).forEach((key) => {
45
+ if (voiceControl[key] === '') {
46
+ delete voiceControl[key];
47
+ }
48
+ });
49
+
43
50
  // if voiceControl is empty, return null
44
51
  return Object.keys(voiceControl).length > 0 ? voiceControl : null;
45
52
  }
@@ -102,6 +102,17 @@ function checkSetState (setState, newState) {
102
102
  }
103
103
  }
104
104
 
105
+ /**
106
+ *
107
+ * @param {Request} req
108
+ * @returns {boolean}
109
+ */
110
+ function isUserInteraction (req) {
111
+ return req.isMessage() || req.isPostBack()
112
+ || req.isReferral() || req.isAttachment()
113
+ || req.isTextOrIntent();
114
+ }
115
+
105
116
  /**
106
117
  *
107
118
  * @private
@@ -115,9 +126,7 @@ function checkSetState (setState, newState) {
115
126
  function mergeState (previousState, req, res, senderStateUpdate, firstInTurnover, lastInTurnover) {
116
127
  const state = { ...previousState, ...res.newState };
117
128
 
118
- const isUserEvent = req.isMessage() || req.isPostBack()
119
- || req.isReferral() || req.isAttachment()
120
- || req.isTextOrIntent();
129
+ const isUserEvent = isUserInteraction(req);
121
130
 
122
131
  // reset expectations
123
132
  if (isUserEvent && !res.newState._expected) {
@@ -224,5 +233,6 @@ module.exports = {
224
233
  VAR_TYPES,
225
234
  mergeState,
226
235
  vars,
227
- checkSetState
236
+ checkSetState,
237
+ isUserInteraction
228
238
  };