wingbot 3.38.1 → 3.39.0-alpha.2

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.38.1",
3
+ "version": "3.39.0-alpha.2",
4
4
  "description": "Enterprise Messaging Bot Conversation Engine",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/src/AiMatching.js CHANGED
@@ -5,6 +5,7 @@
5
5
 
6
6
  const { replaceDiacritics } = require('./utils/tokenizer');
7
7
  const { vars } = require('./utils/stateVariables');
8
+ const stateData = require('./utils/stateData');
8
9
 
9
10
  /** @typedef {import('handlebars')} Handlebars */
10
11
 
@@ -22,6 +23,20 @@ const FULL_EMOJI_REGEX = /^#((?:[\u2600-\u27bf].?|(?:\ud83c[\udde6-\uddff]){2}|[
22
23
  const HAS_CLOSING_HASH = /^#(.+)#$/;
23
24
  const ENTITY_REGEX = /^@([^=><!?]+)(\?)?([!=><]{1,2})?([^=><!]+)?$/i;
24
25
 
26
+ /**
27
+ * RegExp to test a string for a ISO 8601 Date spec
28
+ * YYYY
29
+ * YYYY-MM
30
+ * YYYY-MM-DD
31
+ * YYYY-MM-DDThh:mmTZD
32
+ * YYYY-MM-DDThh:mm:ssTZD
33
+ * YYYY-MM-DDThh:mm:ss.sTZD
34
+ *
35
+ * @see https://www.w3.org/TR/NOTE-datetime
36
+ * @type {RegExp}
37
+ */
38
+ const ISO_8601_REGEX = /^\d{4}-\d\d-\d\d(T\d\d:\d\d(:\d\d)?(\.\d+)?(([+-]\d\d:\d\d)|Z)?)?$/i;
39
+
25
40
  /**
26
41
  * @typedef {string} Compare
27
42
  */
@@ -88,7 +103,9 @@ const COMPARE = {
88
103
  * @prop {Function} text
89
104
  * @prop {Intent[]|null} intents
90
105
  * @prop {Entity[]} entities
106
+ * @prop {object} [configuration]
91
107
  * @prop {object} [state]
108
+ * @prop {Function} [actionData]
92
109
  */
93
110
 
94
111
  /**
@@ -162,6 +179,9 @@ class AiMatching {
162
179
 
163
180
  _normalizeToNumber (value, returnIfEmpty = null) {
164
181
  if (typeof value === 'string') {
182
+ if (value.match(ISO_8601_REGEX)) {
183
+ return value;
184
+ }
165
185
  const flt = parseFloat(value);
166
186
  return Number.isNaN(flt) ? returnIfEmpty : flt;
167
187
  }
@@ -171,15 +191,20 @@ class AiMatching {
171
191
  return returnIfEmpty;
172
192
  }
173
193
 
174
- _hbsOrFn (value, cb) {
175
- if (typeof value === 'string' && value.match(/\{\{.+\}\}/)) {
176
- const compiler = handlebars.compile(value);
177
- return (data) => {
178
- const res = compiler(data);
179
- return cb(res);
180
- };
194
+ _hbsOrFn (value) {
195
+ if (typeof value === 'string') {
196
+ let useValue = value;
197
+ if (useValue.match(/^\$[a-zA-Z0-9_-]+$/)) {
198
+ useValue = `{{${useValue}}}`;
199
+ }
200
+ if (useValue.match(/\{\{.+\}\}/)) {
201
+ const compiler = handlebars.compile(useValue);
202
+ // @ts-ignore
203
+ compiler.template = useValue;
204
+ return compiler;
205
+ }
181
206
  }
182
- return cb(value);
207
+ return value;
183
208
  }
184
209
 
185
210
  _normalizeComparisonArray (compare, op) {
@@ -194,7 +219,7 @@ class AiMatching {
194
219
  const [val] = arr;
195
220
 
196
221
  return [
197
- this._hbsOrFn(val, (r) => this._normalizeToNumber(r, 0))
222
+ this._hbsOrFn(val)
198
223
  ];
199
224
  }
200
225
 
@@ -202,12 +227,12 @@ class AiMatching {
202
227
  const [min, max] = arr;
203
228
 
204
229
  return [
205
- this._hbsOrFn(min, (r) => this._normalizeToNumber(r, -Infinity)),
206
- this._hbsOrFn(max, (r) => this._normalizeToNumber(r, Infinity))
230
+ this._hbsOrFn(min),
231
+ this._hbsOrFn(max)
207
232
  ];
208
233
  }
209
234
 
210
- return arr.map((cmp) => this._hbsOrFn(cmp, (r) => `${r}`));
235
+ return arr.map((cmp) => this._hbsOrFn(cmp));
211
236
  }
212
237
 
213
238
  _stringOpToOperation (op) {
@@ -259,6 +284,7 @@ class AiMatching {
259
284
  */
260
285
  getSetStateForEntityRules ({ entities }) {
261
286
  return entities.reduce((o, rule) => {
287
+
262
288
  if (rule instanceof RegExp) {
263
289
  return o;
264
290
  }
@@ -271,13 +297,13 @@ class AiMatching {
271
297
 
272
298
  if (rule.op === COMPARE.EQUAL
273
299
  && rule.compare
274
- && rule.compare.length === 1
275
- && typeof rule.compare[0] !== 'function') {
300
+ && rule.compare.length === 1) {
276
301
 
277
302
  const key = `@${rule.entity}`;
278
303
  const value = rule.compare[0];
279
304
 
280
- return Object.assign(o, vars.dialogContext(key, value));
305
+ // @ts-ignore
306
+ return vars.dialogContext(key, value && (value.template || value));
281
307
  }
282
308
  return o;
283
309
  }, {});
@@ -427,7 +453,7 @@ class AiMatching {
427
453
 
428
454
  let useState;
429
455
  if (stateless || intents.length === 0) {
430
- useState = Object.entries(req.state)
456
+ useState = Object.entries(stateData(req))
431
457
  .reduce((o, [k, v]) => {
432
458
  if (k.startsWith('@')) {
433
459
  return o;
@@ -435,7 +461,7 @@ class AiMatching {
435
461
  return Object.assign(o, { [k]: v });
436
462
  }, {});
437
463
  } else {
438
- useState = req.state;
464
+ useState = stateData(req);
439
465
  }
440
466
 
441
467
  const { score, handicap, matched } = this
@@ -706,9 +732,14 @@ class AiMatching {
706
732
  : false;
707
733
  }
708
734
 
709
- const useCmp = (compare || []).map((c) => (typeof c === 'function'
710
- ? c(requestState)
711
- : c));
735
+ let useCmp = (compare || [])
736
+ .map((c) => (typeof c === 'function'
737
+ ? c(requestState)
738
+ : c));
739
+
740
+ if ([COMPARE.EQUAL, COMPARE.NOT_EQUAL].includes(operation)) {
741
+ useCmp = useCmp.map((c) => (typeof c === 'string' ? c : `${c}`));
742
+ }
712
743
 
713
744
  switch (operation) {
714
745
  case COMPARE.EQUAL:
@@ -721,7 +752,8 @@ class AiMatching {
721
752
  if (normalized === null) {
722
753
  return false;
723
754
  }
724
- return normalized >= min && normalized <= max;
755
+ return normalized >= this._normalizeToNumber(min, -Infinity)
756
+ && normalized <= this._normalizeToNumber(max, Infinity);
725
757
  }
726
758
  case COMPARE.GT:
727
759
  case COMPARE.LT:
@@ -732,7 +764,7 @@ class AiMatching {
732
764
  if (normalized === null) {
733
765
  return false;
734
766
  }
735
- return this._numberComparison(op, cmp, normalized);
767
+ return this._numberComparison(op, this._normalizeToNumber(cmp, 0), normalized);
736
768
  }
737
769
  default:
738
770
  return true;
@@ -740,6 +772,9 @@ class AiMatching {
740
772
  }
741
773
 
742
774
  _numberComparison (op, cmp, normalized) {
775
+ if (typeof cmp !== typeof normalized) {
776
+ return false;
777
+ }
743
778
  switch (op) {
744
779
  case COMPARE.GT:
745
780
  return normalized > cmp;
package/src/Request.js CHANGED
@@ -1137,7 +1137,15 @@ class Request {
1137
1137
  let { setState = {} } = res;
1138
1138
  for (const gi of this.globalIntents.values()) {
1139
1139
  if (gi.action === res.action) {
1140
- ({ entitiesSetState } = gi);
1140
+ entitiesSetState = { ...gi.entitiesSetState };
1141
+
1142
+ const values = Array.from(Object.values(entitiesSetState));
1143
+
1144
+ for (const value of values) {
1145
+ if (typeof value === 'function') {
1146
+ Object.assign(entitiesSetState, value(stateData(this)));
1147
+ }
1148
+ }
1141
1149
  }
1142
1150
  }
1143
1151
  const newState = {
@@ -0,0 +1,50 @@
1
+ /**
2
+ * @author David Menger
3
+ */
4
+ 'use strict';
5
+
6
+ /**
7
+ *
8
+ * @param {Date} inputDate
9
+ * @param {boolean} toZeroTimezone
10
+ * @returns {string}
11
+ */
12
+ function dateToISO8601String (inputDate, toZeroTimezone = false) {
13
+ const padDigits = function padDigits (number, digits) {
14
+ return Array(Math.max(digits - String(number).length + 1, 0)).join('0') + number;
15
+ };
16
+
17
+ let d = inputDate;
18
+ const offsetMinutes = d.getTimezoneOffset();
19
+ const offsetHours = offsetMinutes / 60;
20
+ let offset = 'Z';
21
+ if (toZeroTimezone) {
22
+ d = new Date(d.toISOString());
23
+ d.setMinutes(d.getMinutes() + offsetMinutes);
24
+ } else if (offsetHours < 0) {
25
+ offset = `+${padDigits(`${offsetHours.toString().replace('-', '')}00`, 4)}`;
26
+ } else if (offsetHours > 0) {
27
+ offset = `-${padDigits(`${offsetHours}00`, 4)}`;
28
+ }
29
+
30
+ return `${d.getFullYear()
31
+ }-${padDigits((d.getMonth() + 1), 2)
32
+ }-${padDigits(d.getDate(), 2)
33
+ }T${padDigits(d.getHours(), 2)
34
+ }:${padDigits(d.getMinutes(), 2)
35
+ }:${padDigits(d.getSeconds(), 2)
36
+ }.${padDigits(d.getMilliseconds(), 2)
37
+ }${offset}`;
38
+ }
39
+
40
+ /**
41
+ * Make zero date
42
+ *
43
+ * @param {Date} d
44
+ * @returns {Date}
45
+ */
46
+ function zeroHourDate (d) {
47
+ return new Date(d.getFullYear(), d.getMonth(), d.getDate(), 0, -d.getTimezoneOffset());
48
+ }
49
+
50
+ module.exports = { dateToISO8601String, zeroHourDate };
@@ -208,6 +208,7 @@ function makeQuickReplies (replies, path = '', translate = (w) => w, quickReplyC
208
208
  }
209
209
  return o;
210
210
  }, {});
211
+
211
212
  setState = {
212
213
  ...entitiesSetState,
213
214
  ...setState
@@ -6,9 +6,19 @@
6
6
  /** @typedef {import('../Request')} Request */
7
7
  /** @typedef {import('../Responder')} Responder */
8
8
 
9
+ const { dateToISO8601String, zeroHourDate } = require('./datetime');
10
+
11
+ /**
12
+ * @typedef {object} IStateRequest
13
+ * @prop {object} [state]
14
+ * @prop {object} [configuration]
15
+ * @prop {Function} text
16
+ * @prop {Function} [actionData]
17
+ */
18
+
9
19
  /**
10
20
  *
11
- * @param {Request} req
21
+ * @param {IStateRequest} req
12
22
  * @param {Responder} res
13
23
  * @param {object} configuration
14
24
  * @param {object} [stateOverride]
@@ -19,6 +29,15 @@ module.exports = function stateData (req, res = null, configuration = null, stat
19
29
 
20
30
  const $this = req.text();
21
31
 
32
+ const now = new Date();
33
+
34
+ const $now = dateToISO8601String(now, true);
35
+ const $today = dateToISO8601String(zeroHourDate(now), true);
36
+ now.setDate(now.getDate() + 1);
37
+ const $tomorrow = dateToISO8601String(zeroHourDate(now), true);
38
+ now.setDate(now.getDate() - 2);
39
+ const $yesterday = dateToISO8601String(zeroHourDate(now), true);
40
+
22
41
  return {
23
42
  c,
24
43
  configuration: c,
@@ -26,7 +45,12 @@ module.exports = function stateData (req, res = null, configuration = null, stat
26
45
  ...(res ? res.newState : {}),
27
46
  ...stateOverride,
28
47
  $this,
29
- ...req.actionData(),
48
+ $now,
49
+ $today,
50
+ $tomorrow,
51
+ $yesterday,
52
+ // yes - res because of circular dependency
53
+ ...(res && req.actionData()),
30
54
  ...(res ? res.data : {}),
31
55
  $input: $this
32
56
  };