wingbot 3.56.0 → 3.57.0

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.56.0",
3
+ "version": "3.57.0",
4
4
  "description": "Enterprise Messaging Bot Conversation Engine",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -53,7 +53,7 @@
53
53
  "tsd-jsdoc": "^2.5.0"
54
54
  },
55
55
  "dependencies": {
56
- "@amplitude/ua-parser-js": "^0.7.31",
56
+ "@amplitude/ua-parser-js": "^0.7.33",
57
57
  "deep-extend": "^0.6.0",
58
58
  "form-data": "^4.0.0",
59
59
  "graphql": "^15.8.0",
@@ -0,0 +1,38 @@
1
+ /**
2
+ * @author wingbot.ai
3
+ */
4
+ 'use strict';
5
+
6
+ const { compileWithState } = require('../../src/utils');
7
+
8
+ function personaPlugin (params) {
9
+
10
+ function persona (req, res) {
11
+ let name = compileWithState(req, res, params.name).trim();
12
+ let img = compileWithState(req, res, params.img).trim();
13
+
14
+ if (!name && !img) {
15
+ res.setPersona(null);
16
+ return;
17
+ }
18
+
19
+ if (name) {
20
+ const foundPersona = res._findPersonaConfiguration(name) || {};
21
+ if (!img) {
22
+ img = foundPersona.profile_pic_url;
23
+ }
24
+ if (foundPersona.name) {
25
+ name = foundPersona.name;
26
+ }
27
+ }
28
+
29
+ res.setPersona({
30
+ ...(img ? { profile_pic_url: img } : {}),
31
+ ...(name ? { name } : {})
32
+ });
33
+ }
34
+
35
+ return persona;
36
+ }
37
+
38
+ module.exports = personaPlugin;
@@ -564,6 +564,29 @@
564
564
  "description": "If API call fails"
565
565
  }
566
566
  ]
567
+ },
568
+ {
569
+ "id": "ai.wingbot.persona",
570
+ "name": "Set persona",
571
+ "description": "Use this persona for following messages.",
572
+ "availableSince": 3.56,
573
+ "editable": false,
574
+ "isFactory": true,
575
+ "category": "conversation",
576
+ "inputs": [
577
+ {
578
+ "name": "name",
579
+ "label": "Persona name",
580
+ "type": "text"
581
+ },
582
+ {
583
+ "name": "img",
584
+ "label": "Persona image",
585
+ "type": "text"
586
+ }
587
+ ],
588
+ "items": [
589
+ ]
567
590
  }
568
591
  ],
569
592
  "categories": [
@@ -17,10 +17,12 @@ const { shouldExecuteResolver } = require('./resolvers/resolverTags');
17
17
 
18
18
  const MESSAGE_RESOLVER_NAME = 'botbuild.message';
19
19
 
20
+ /** @typedef {import('./Router').BaseConfiguration} BaseConfiguration */
21
+
20
22
  /**
21
23
  *
22
24
  * @template {object} [S=object]
23
- * @template {object} [C=object]
25
+ * @template {BaseConfiguration} [C=object]
24
26
  * @typedef {import('./Router').Middleware<S,C>} Middleware
25
27
  */
26
28
 
@@ -111,7 +113,7 @@ const DUMMY_ROUTE = { id: 0, path: null, resolvers: [] };
111
113
  */
112
114
 
113
115
  /**
114
- * @template {object} [C=object]
116
+ * @template {BaseConfiguration} [C=object]
115
117
  * @typedef {object} BotContext
116
118
  * @prop {LinkTranslator} [linksTranslator] - function, that translates links globally
117
119
  * @prop {ConfigStorage} [configStorage] - function, that translates links globally
@@ -133,14 +135,14 @@ const DUMMY_ROUTE = { id: 0, path: null, resolvers: [] };
133
135
  * @prop {string} [staticBlockId]
134
136
  * @prop {Block[]} [blocks]
135
137
  * @prop {object} [BuildRouter]
136
- * @prop {string} [resolverId] - only for text messages with random characters
138
+ * @prop {string|number} [resolverId] - only for text messages with random characters
137
139
  */
138
140
 
139
141
  /**
140
142
  * Build bot from Wingbot configuration file or snapshot url
141
143
  *
142
144
  * @template {object} [S=object]
143
- * @template {object} [C=object]
145
+ * @template {BaseConfiguration} [C=object]
144
146
  * @class BuildRouter
145
147
  * @extends {Router<S,C>}
146
148
  */
@@ -182,7 +184,7 @@ class BuildRouter extends Router {
182
184
  * module.exports = bot;
183
185
  */
184
186
  constructor (block, plugins = new Plugins(), context = {}, fetchFn = fetch) {
185
- super();
187
+ super(context.configuration);
186
188
 
187
189
  this._validateBlock(block);
188
190
 
@@ -191,10 +193,6 @@ class BuildRouter extends Router {
191
193
  /** @type {BotContext<C>} */
192
194
  this._context = context;
193
195
 
194
- /** @type {C} */
195
- // @ts-ignore
196
- this._configuration = context.configuration || {};
197
-
198
196
  /** @type {LinksMap} */
199
197
  this._linksMap = new Map();
200
198
 
@@ -249,13 +247,6 @@ class BuildRouter extends Router {
249
247
  return this._botId;
250
248
  }
251
249
 
252
- /**
253
- * @returns {C}
254
- */
255
- get configuration () {
256
- return this._configuration;
257
- }
258
-
259
250
  _validateBlock (block, action = 'build') {
260
251
  // @ts-ignore
261
252
  assert.ok(block && typeof block === 'object', `Bot ${action} failed: expected the block to be an object, ${typeof block} given`);
@@ -650,14 +641,6 @@ class BuildRouter extends Router {
650
641
  });
651
642
  }
652
643
 
653
- /**
654
- *
655
- * @returns {object}
656
- */
657
- getConfiguration () {
658
- return this._configuration;
659
- }
660
-
661
644
  /**
662
645
  *
663
646
  * @param {LinksMap} linksMap
@@ -911,7 +894,7 @@ class BuildRouter extends Router {
911
894
  }
912
895
 
913
896
  /**
914
- * @template {object} [C=object]
897
+ * @template {BaseConfiguration} [C=object]
915
898
  * @param {Block[]} blocks - blocks list
916
899
  * @param {Plugins} [plugins]
917
900
  * @param {BotContext<C>} [context]
package/src/Plugins.js CHANGED
@@ -10,16 +10,18 @@ const plugins = require('../plugins');
10
10
  const RouterWrap = require('./RouterWrap');
11
11
  const wrapPluginFunction = require('./utils/wrapPluginFunction');
12
12
 
13
+ /** @typedef {import('./Router').BaseConfiguration} BaseConfiguration */
14
+
13
15
  /**
14
16
  * @template {object} [S=object]
15
- * @template {object} [C=object]
17
+ * @template {BaseConfiguration} [C=object]
16
18
  * @typedef {import('./Router').Middleware<S,C>} Middleware
17
19
  */
18
20
 
19
21
  /**
20
22
  *
21
23
  * @template {object} [S=object]
22
- * @template {object} [C=object]
24
+ * @template {BaseConfiguration} [C=object]
23
25
  * @callback Plugin
24
26
  * @param {Request<S,C>} req
25
27
  * @param {Responder} res
@@ -31,7 +33,7 @@ const wrapPluginFunction = require('./utils/wrapPluginFunction');
31
33
  /**
32
34
  *
33
35
  * @template {object} [S=object]
34
- * @template {object} [C=object]
36
+ * @template {BaseConfiguration} [C=object]
35
37
  * @callback PluginFactory
36
38
  * @param {object} params
37
39
  * @param {C} configuration
package/src/Processor.js CHANGED
@@ -657,7 +657,14 @@ class Processor extends EventEmitter {
657
657
  pageId
658
658
  };
659
659
 
660
- res = new Responder(senderId, messageSender, token, options, responderData);
660
+ res = new Responder(
661
+ senderId,
662
+ messageSender,
663
+ token,
664
+ options,
665
+ responderData,
666
+ configuration
667
+ );
661
668
  const postBack = this._createPostBack(postbackAcumulator, req, res, features);
662
669
 
663
670
  let continueDispatching = true;
package/src/Request.js CHANGED
@@ -38,6 +38,8 @@ function makeTimestamp () {
38
38
  return now;
39
39
  }
40
40
 
41
+ /** @typedef {import('./Router').BaseConfiguration} BaseConfiguration */
42
+
41
43
  /**
42
44
  * @typedef {object} Entity
43
45
  * @prop {string} entity
@@ -117,7 +119,7 @@ function makeTimestamp () {
117
119
  * Instance of {Request} class is passed as first parameter of handler (req)
118
120
  *
119
121
  * @template {object} [S=object]
120
- * @template {object} [C=object]
122
+ * @template {BaseConfiguration} [C=object]
121
123
  * @class Request
122
124
  */
123
125
  class Request {
package/src/Responder.js CHANGED
@@ -8,7 +8,7 @@ const ReceiptTemplate = require('./templates/ReceiptTemplate');
8
8
  const ButtonTemplate = require('./templates/ButtonTemplate');
9
9
  const GenericTemplate = require('./templates/GenericTemplate');
10
10
  const ListTemplate = require('./templates/ListTemplate');
11
- const { makeAbsolute, makeQuickReplies } = require('./utils');
11
+ const { makeAbsolute, makeQuickReplies, tokenize } = require('./utils');
12
12
  const { ResponseFlag } = require('./analytics/consts');
13
13
  const { checkSetState } = require('./utils/stateVariables');
14
14
  const {
@@ -81,6 +81,14 @@ Object.freeze(ExpectedInput);
81
81
  * @returns {VoiceControl}
82
82
  */
83
83
 
84
+ /**
85
+ * @typedef {object} Persona
86
+ * @prop {string} [profile_pic_url]
87
+ * @prop {string} [name]
88
+ */
89
+
90
+ const PERSONA_DEFAULT = '_default';
91
+
84
92
  /**
85
93
  * Instance of responder is passed as second parameter of handler (res)
86
94
  *
@@ -88,11 +96,19 @@ Object.freeze(ExpectedInput);
88
96
  */
89
97
  class Responder {
90
98
 
91
- constructor (senderId, messageSender, token = null, options = {}, data = {}) {
99
+ constructor (
100
+ senderId,
101
+ messageSender,
102
+ token = null,
103
+ options = {},
104
+ data = {},
105
+ configuration = {}
106
+ ) {
92
107
  this._messageSender = messageSender;
93
108
  this._senderId = senderId;
94
109
  this._pageId = options.pageId;
95
110
  this.token = token;
111
+ this._configuration = configuration;
96
112
 
97
113
  /**
98
114
  * The empty object, which is filled with res.setState() method
@@ -185,6 +201,21 @@ class Responder {
185
201
  this._typingSent = false;
186
202
  }
187
203
 
204
+ _findPersonaConfiguration (name) {
205
+ if (!name || !this._configuration.persona) {
206
+ return null;
207
+ }
208
+ if (!this._configuration._cachedPersonas) {
209
+ // eslint-disable-next-line no-param-reassign
210
+ this._configuration._cachedPersonas = new Map(
211
+ Object.entries(this._configuration.persona)
212
+ .map(([k, v]) => [k === PERSONA_DEFAULT ? k : tokenize(k), v])
213
+ );
214
+ }
215
+ const nameKey = name === PERSONA_DEFAULT ? PERSONA_DEFAULT : tokenize(name);
216
+ return this._configuration._cachedPersonas.get(nameKey);
217
+ }
218
+
188
219
  /**
189
220
  *
190
221
  * Returns current conversation transcript
@@ -283,6 +314,7 @@ class Responder {
283
314
  if (!data || typeof data !== 'object') {
284
315
  throw new Error('Send method requires an object as first param');
285
316
  }
317
+ this.setPersona(PERSONA_DEFAULT);
286
318
  if (!data.recipient) {
287
319
  Object.assign(data, {
288
320
  recipient: {
@@ -414,10 +446,23 @@ class Responder {
414
446
  /**
415
447
  * Tets the persona for following requests
416
448
  *
417
- * @param {object|string|null} personaId
449
+ * @param {Persona|string|null} personaId
418
450
  * @returns {this}
419
451
  */
420
452
  setPersona (personaId = null) {
453
+ if (personaId === PERSONA_DEFAULT && this._persona) {
454
+ return this;
455
+ }
456
+ if (typeof personaId === 'string') {
457
+ const persona = this._findPersonaConfiguration(personaId);
458
+ if (persona) {
459
+ this._persona = persona;
460
+ return this;
461
+ }
462
+ if (personaId === PERSONA_DEFAULT) {
463
+ return this;
464
+ }
465
+ }
421
466
  this._persona = personaId;
422
467
  return this;
423
468
  }
@@ -1345,4 +1390,6 @@ Responder.TYPE_MESSAGE_TAG = TYPE_MESSAGE_TAG;
1345
1390
  Responder.TYPE_UPDATE = TYPE_UPDATE;
1346
1391
  Responder.TYPE_RESPONSE = TYPE_RESPONSE;
1347
1392
 
1393
+ Responder.PERSONA_DEFAULT = PERSONA_DEFAULT;
1394
+
1348
1395
  module.exports = Responder;
package/src/Router.js CHANGED
@@ -31,7 +31,7 @@ function defaultPathContext () {
31
31
 
32
32
  /**
33
33
  * @template {object} [S=object]
34
- * @template {object} [C=object]
34
+ * @template {BaseConfiguration} [C=object]
35
35
  * @callback Resolver processing function
36
36
  * @param {Request<S,C>} [req]
37
37
  * @param {Responder} [res]
@@ -41,7 +41,7 @@ function defaultPathContext () {
41
41
 
42
42
  /**
43
43
  * @template {object} [S=object]
44
- * @template {object} [C=object]
44
+ * @template {BaseConfiguration} [C=object]
45
45
  * @callback Reduce processing function
46
46
  * @param {Request<S,C>} [req]
47
47
  * @param {Responder} [res]
@@ -52,7 +52,7 @@ function defaultPathContext () {
52
52
 
53
53
  /**
54
54
  * @template {object} [S=object]
55
- * @template {object} [C=object]
55
+ * @template {BaseConfiguration} [C=object]
56
56
  * @typedef {object} IRouter
57
57
  * @prop {Reduce<S,C>} reduce
58
58
  */
@@ -69,28 +69,63 @@ function defaultPathContext () {
69
69
  /**
70
70
  *
71
71
  * @template {object} [S=object]
72
- * @template {object} [C=object]
72
+ * @template {BaseConfiguration} [C=object]
73
73
  * @typedef {Resolver<S,C>|RouteExp|IRouter} Middleware flow control statement or function
74
74
  */
75
75
 
76
+ /**
77
+ * @typedef {import('./Responder').Persona} Persona
78
+ */
79
+
80
+ /**
81
+ * @typedef {Object<string|'_default',PersonConfiguration>} PersonConfiguration
82
+ */
83
+
84
+ /**
85
+ * @typedef {object} BaseConfiguration
86
+ * @prop {PersonConfiguration} [persona]
87
+ */
88
+
76
89
  /**
77
90
  * Cascading router
78
91
  *
79
92
  * @template {object} [S=object]
80
- * @template {object} [C=object]
93
+ * @template {BaseConfiguration} [C=object]
81
94
  * @class Router
82
95
  * @extends {ReducerWrapper}
83
96
  */
84
97
  class Router extends ReducerWrapper {
85
98
 
86
- constructor () {
99
+ /**
100
+ * @param {C} [configuration]
101
+ */
102
+ constructor (configuration = null) {
87
103
  super();
88
104
 
105
+ /** @type {C} */
106
+ // @ts-ignore
107
+ this._configuration = configuration || {};
108
+
89
109
  this._routes = [];
90
110
 
91
111
  this.globalIntents = new Map();
92
112
  }
93
113
 
114
+ /**
115
+ * @returns {C}
116
+ */
117
+ get configuration () {
118
+ return this._configuration;
119
+ }
120
+
121
+ /**
122
+ *
123
+ * @returns {C}
124
+ */
125
+ getConfiguration () {
126
+ return this._configuration;
127
+ }
128
+
94
129
  _normalizePath (path) {
95
130
  let normalizedPath;
96
131
 
@@ -470,7 +470,7 @@ function onInteractionHandler (
470
470
  isGoto: false,
471
471
  withUser,
472
472
  ...langsExtension,
473
- ...(hasExtendedEvents ? {} : actionMeta)
473
+ ...actionMeta
474
474
  });
475
475
 
476
476
  let prevAction = action;
@@ -22,7 +22,7 @@ class AnyResponseAssert {
22
22
  /**
23
23
  * Checks, that response contains a text
24
24
  *
25
- * @param {string} search
25
+ * @param {string|object} search
26
26
  * @returns {this}
27
27
  *
28
28
  * @memberOf ResponseAssert
@@ -4,6 +4,7 @@
4
4
  'use strict';
5
5
 
6
6
  const assert = require('assert');
7
+ const deepExtend = require('deep-extend');
7
8
  const { actionMatches, parseActionPayload } = require('../utils');
8
9
 
9
10
  /**
@@ -109,11 +110,26 @@ function searchMatchesText (search, text) {
109
110
  * Checks, that text contain a message
110
111
  *
111
112
  * @param {object} response
112
- * @param {string} search
113
+ * @param {string|object} search
113
114
  * @param {string|false} [message='Should contain a text'] - use false for no asserts
114
115
  * @returns {boolean}
115
116
  */
116
117
  function contains (response, search, message = 'Should contain a text') {
118
+ if (typeof search === 'object') {
119
+ try {
120
+ assert.deepEqual(
121
+ response,
122
+ deepExtend({}, response, search),
123
+ 'Event contains'
124
+ );
125
+ return true;
126
+ } catch (e) {
127
+ if (message === false) {
128
+ return false;
129
+ }
130
+ throw e;
131
+ }
132
+ }
117
133
  const text = getText(response);
118
134
  const typeIsText = typeof text === 'string';
119
135
  if (message === false && !typeIsText) {
@@ -294,7 +310,7 @@ function buttonTemplate (response, search, count = null, message = 'Should conta
294
310
 
295
311
  /**
296
312
  *
297
- * @param {search} response
313
+ * @param {object} response
298
314
  * @param {string|false} message
299
315
  * @returns {false|object[]}
300
316
  */
@@ -315,7 +331,7 @@ function genericTemplateItems (response, message = 'Generic template should cont
315
331
  /**
316
332
  * Validates generic template
317
333
  *
318
- * @param {search} response
334
+ * @param {object} response
319
335
  * @param {number} [count]
320
336
  * @param {string|false} message
321
337
  * @returns {boolean}
@@ -38,8 +38,14 @@ function compileWithState (req, res, template) {
38
38
  if (!template) {
39
39
  return '';
40
40
  }
41
+ if (typeof template !== 'string') {
42
+ return `${template}`;
43
+ }
44
+ if (!template.includes('{{')) {
45
+ return template;
46
+ }
41
47
  const data = stateData(req, res);
42
- return handlebars.compile(`${template}`)(data);
48
+ return handlebars.compile(template)(data);
43
49
  }
44
50
 
45
51
  module.exports = compileWithState;