wingbot 3.32.0-alpha.1 → 3.33.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.32.0-alpha.1",
3
+ "version": "3.33.0",
4
4
  "description": "Enterprise Messaging Bot Conversation Engine",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -9,7 +9,7 @@
9
9
  "doc": "npm run doc:gql && node ./bin/makeApiDoc.js && cpy ./CHANGELOG.md ./doc && gitbook install ./doc && gitbook build ./doc && rimraf -rf ./docs && rimraf --rf ./doc/CHANGELOG.md && move-cli ./doc/_book ./docs",
10
10
  "test": "npm run test:lint && npm run test:coverage && npm run test:coverage:threshold",
11
11
  "test:coverage": "nyc --reporter=html mocha ./test && nyc report",
12
- "test:coverage:threshold": "nyc check-coverage --lines 90 --functions 90 --branches 80",
12
+ "test:coverage:threshold": "nyc check-coverage --lines 91 --functions 90 --branches 82",
13
13
  "test:backend": "mocha ./test",
14
14
  "test:lint": "eslint --ext .js src test *.js plugins"
15
15
  },
@@ -57,14 +57,12 @@
57
57
  "graphql": "^15.8.0",
58
58
  "jsonwebtoken": "^8.5.1",
59
59
  "node-fetch": "^2.6.7",
60
- "path-to-regexp": "^6.2.0",
60
+ "path-to-regexp": "^6.2.1",
61
61
  "uuid": "^8.3.2",
62
62
  "webalize": "^0.1.0"
63
63
  },
64
64
  "optionalDependencies": {
65
65
  "axios": "^0.21.1",
66
- "handlebars": "^4.0.0",
67
- "request": "^2.88.0",
68
- "request-promise-native": "^1.0.7"
66
+ "handlebars": "^4.0.0"
69
67
  }
70
68
  }
package/src/BotApp.js CHANGED
@@ -23,6 +23,7 @@ const DEFAULT_API_URL = 'https://orchestrator-api.wingbot.ai';
23
23
 
24
24
  /** @typedef {import('./CallbackAuditLog')} AuditLog */
25
25
  /** @typedef {import('./BotAppSender').TlsOptions} TlsOptions */
26
+ /** @typedef {import('./ReturnSender').ReturnSenderOptions} ReturnSenderOptions */
26
27
 
27
28
  /**
28
29
  * @typedef {object} BotAppOptions
@@ -35,7 +36,7 @@ const DEFAULT_API_URL = 'https://orchestrator-api.wingbot.ai';
35
36
  * @prop {AuditLog} [auditLog]
36
37
  * @prop {TlsOptions} [tls]
37
38
  *
38
- * @typedef {ProcessorOptions & BotAppOptions} Options
39
+ * @typedef {ProcessorOptions & BotAppOptions & ReturnSenderOptions} Options
39
40
  */
40
41
 
41
42
  /**
@@ -66,9 +67,20 @@ class BotApp {
66
67
  preferSynchronousResponse = false,
67
68
  auditLog = null,
68
69
  tls = null,
70
+
71
+ textFilter,
72
+ logStandbyEvents,
73
+ confidentInputFilter,
74
+
69
75
  ...processorOptions
70
76
  } = options;
71
77
 
78
+ this._returnSenderOptions = {
79
+ textFilter,
80
+ logStandbyEvents,
81
+ confidentInputFilter
82
+ };
83
+
72
84
  this._secret = Promise.resolve(secret);
73
85
  this._fetch = fetch; // mock
74
86
  this._appId = appId;
@@ -197,16 +209,18 @@ class BotApp {
197
209
  sync = false,
198
210
  headers = {}
199
211
  ) {
200
- const { mid = null } = message;
212
+ const setResponseToMid = message.response_to_mid || message.mid;
201
213
 
202
214
  if (sync || this._preferSynchronousResponse) {
203
- const sender = new ReturnSender({}, senderId, message, this._senderLogger);
215
+ const options = this._returnSenderOptions;
216
+ const sender = new ReturnSender(options, senderId, message, this._senderLogger);
204
217
  sender.propagatesWaitEvent = true;
205
218
  const res = await this._processor.processMessage(message, pageId, sender, { appId });
206
219
  await this._processSenderResponses(sender, senderId, pageId, headers);
207
220
 
208
221
  return {
209
222
  status: res.status,
223
+ // yes, it should be just mid
210
224
  response_to_mid: message.mid,
211
225
  messaging: sender.responses
212
226
  .map((response) => {
@@ -216,8 +230,8 @@ class BotApp {
216
230
  }
217
231
 
218
232
  // attach response_to_mid
219
- if (typeof response.response_to_mid === 'undefined' && mid) {
220
- Object.assign(response, { response_to_mid: mid });
233
+ if (typeof response.response_to_mid === 'undefined' && setResponseToMid) {
234
+ Object.assign(response, { response_to_mid: setResponseToMid });
221
235
  }
222
236
 
223
237
  return response;
@@ -226,11 +240,12 @@ class BotApp {
226
240
  }
227
241
 
228
242
  const options = {
243
+ ...this._returnSenderOptions,
229
244
  apiUrl: this._apiUrl,
230
245
  pageId,
231
246
  appId,
232
247
  secret,
233
- mid,
248
+ mid: setResponseToMid,
234
249
  fetch: this._fetch,
235
250
  tls: this._tls
236
251
  };
@@ -159,7 +159,6 @@ class BuildRouter extends Router {
159
159
  * @param {ConfigStorage} [context.configStorage] - function, that translates links globally
160
160
  * @param {boolean} [context.allowForbiddenSnippetWords] - disable security rule
161
161
  * @param {RouteConfig[]} [context.routeConfigs] - list of disabled routes
162
- * @param {object} [context.config] - context data
163
162
  * @param {object} [context.configuration]
164
163
  * @param {fetch} [fetchFn] - override a request function
165
164
  * @example
@@ -427,7 +426,8 @@ class BuildRouter extends Router {
427
426
  this._configTs = 0;
428
427
  }
429
428
  if (this._prebuiltGlobalIntents !== null) {
430
- this.globalIntents = new Map(this._prebuiltGlobalIntents);
429
+ this.globalIntents.clear();
430
+ this._prebuiltGlobalIntents.forEach(([k, v]) => this.globalIntents.set(k, v));
431
431
  }
432
432
  }
433
433
 
@@ -449,7 +449,8 @@ class BuildRouter extends Router {
449
449
  if (this._prebuiltGlobalIntents === null) {
450
450
  this._prebuiltGlobalIntents = Array.from(this.globalIntents.entries());
451
451
  } else {
452
- this.globalIntents = new Map(this._prebuiltGlobalIntents);
452
+ this.globalIntents.clear();
453
+ this._prebuiltGlobalIntents.forEach(([k, v]) => this.globalIntents.set(k, v));
453
454
  }
454
455
 
455
456
  if (this._prebuiltRoutesCount === null) {
@@ -754,15 +755,21 @@ class BuildRouter extends Router {
754
755
  nextRouteIsSameResponder = nextRoute.respondsToRouteId === route.respondsToRouteId;
755
756
  }
756
757
 
758
+ const buildInfo = {
759
+ expectedToAddResolver: !!route.expectedPath,
760
+ attachedRouter: false
761
+ };
762
+
757
763
  const resolvers = [
758
764
  ...this._buildRouteHead(route, nextRouteIsSameResponder),
759
- ...this.buildResolvers(route.resolvers, route, route.expectedPath)
765
+ ...this.buildResolvers(route.resolvers, route, buildInfo)
760
766
  ];
761
767
 
762
768
  if (route.expectedPath) {
763
769
  // attach expected before last message, if there is
764
770
  resolvers.push(expected({
765
- path: route.expectedPath
771
+ path: route.expectedPath,
772
+ attachedRouter: buildInfo.attachedRouter
766
773
  }, {
767
774
  isLastIndex: true
768
775
  }));
@@ -790,10 +797,10 @@ class BuildRouter extends Router {
790
797
  *
791
798
  * @param {Resolver[]} resolvers
792
799
  * @param {TransformedRoute} route
793
- * @param {*} expectedToAddResolver
800
+ * @param {*} buildInfo
794
801
  * @returns {Middleware[]}
795
802
  */
796
- buildResolvers (resolvers, route = DUMMY_ROUTE, expectedToAddResolver = false) {
803
+ buildResolvers (resolvers, route = DUMMY_ROUTE, buildInfo = {}) {
797
804
  const {
798
805
  path: ctxPath, isFallback, isResponder, expectedPath, id
799
806
  } = route;
@@ -812,7 +819,7 @@ class BuildRouter extends Router {
812
819
 
813
820
  const context = {
814
821
  ...this._context,
815
- isLastIndex: lastIndex === i && !expectedToAddResolver,
822
+ isLastIndex: lastIndex === i && !buildInfo.expectedToAddResolver,
816
823
  isLastMessage: lastMessageIndex === i,
817
824
  router: this,
818
825
  linksMap: this._linksMap,
@@ -824,7 +831,8 @@ class BuildRouter extends Router {
824
831
  configuration
825
832
  };
826
833
 
827
- const resFn = this._resolverFactory(resolver, context);
834
+ const resFn = this._resolverFactory(resolver, context, buildInfo);
835
+
828
836
  // @ts-ignore
829
837
  resFn.configuration = configuration;
830
838
  return resFn;
@@ -835,9 +843,10 @@ class BuildRouter extends Router {
835
843
  *
836
844
  * @param {Resolver} resolver
837
845
  * @param {BotContext} context
846
+ * @param {*} buildInfo
838
847
  * @returns {Middleware}
839
848
  */
840
- _resolverFactory (resolver, context) {
849
+ _resolverFactory (resolver, context, buildInfo) {
841
850
  const { type } = resolver;
842
851
 
843
852
  if (!this.resources.has(type)) {
@@ -848,9 +857,17 @@ class BuildRouter extends Router {
848
857
 
849
858
  const fn = factoryFn(resolver.params, context, this._plugins);
850
859
 
860
+ if (fn.reduce) {
861
+ Object.assign(buildInfo, { attachedRouter: true });
862
+ }
863
+
851
864
  if ([
852
865
  'botbuild.include',
853
- 'botbuild.path'
866
+ 'botbuild.path',
867
+ 'botbuild.customCode',
868
+ 'botbuild.inlineCode',
869
+ 'botbuild.plugin',
870
+ 'botbuild.postback'
854
871
  ].includes(type)) {
855
872
  return fn;
856
873
  }
@@ -524,7 +524,7 @@ class ConversationTester {
524
524
  await tester.text(textCase.text);
525
525
 
526
526
  if (textCase.action) {
527
- tester.passedAction(textCase.action);
527
+ tester.passedAction(textCase.action, true);
528
528
  }
529
529
 
530
530
  if (textCase.appId) {
@@ -623,7 +623,7 @@ class ConversationTester {
623
623
  if (!this._options.disableAssertActions) {
624
624
  passedAction.split('\n')
625
625
  .map((a) => (a.trim().match(/^[a-z\-0-9/_]+$/) ? a : tokenize(a)))
626
- .forEach((a) => a && t.passedAction(a));
626
+ .forEach((a) => a && t.passedAction(a, true));
627
627
  }
628
628
 
629
629
  const any = t.any();
package/src/Request.js CHANGED
@@ -16,6 +16,7 @@ const {
16
16
  FEATURE_SSML,
17
17
  FEATURE_PHRASES,
18
18
  FEATURE_TEXT,
19
+ FEATURE_TRACKING,
19
20
  getDefaultFeatureList
20
21
  } = require('./features');
21
22
 
@@ -259,6 +260,11 @@ class Request {
259
260
  */
260
261
  this.FEATURE_TEXT = FEATURE_TEXT;
261
262
 
263
+ /**
264
+ * @constant {string} FEATURE_TRACKING channel supports tracking protocol
265
+ */
266
+ this.FEATURE_TRACKING = FEATURE_TRACKING;
267
+
262
268
  /** @type {object} */
263
269
  this.configuration = Object.freeze({});
264
270
  }
@@ -837,7 +843,9 @@ class Request {
837
843
  * });
838
844
  */
839
845
  expectedContext (justOnce = false, includeKeywords = false) {
840
- const { _expected: expected, _expectedConfidentInput: confident } = this.state;
846
+ const ad = this.actionData();
847
+ const expected = ad._useExpected || this.state._expected;
848
+ const confident = this.state._expectedConfidentInput;
841
849
 
842
850
  const ret = {};
843
851
 
@@ -1770,4 +1778,9 @@ Request.FEATURE_PHRASES = FEATURE_PHRASES;
1770
1778
  */
1771
1779
  Request.FEATURE_TEXT = FEATURE_TEXT;
1772
1780
 
1781
+ /**
1782
+ * @constant {string} FEATURE_TRACKING channel supports tracking protocol
1783
+ */
1784
+ Request.FEATURE_TRACKING = FEATURE_TRACKING;
1785
+
1773
1786
  module.exports = Request;
package/src/Responder.js CHANGED
@@ -528,6 +528,8 @@ class Responder {
528
528
  if (prepend) Object.assign(prep, { _prepend: true });
529
529
  if (justToExisting) Object.assign(prep, { _justToExisting: true });
530
530
 
531
+ const useCa = this.currentAction();
532
+
531
533
  if (actionIsObject) {
532
534
  this._quickReplyCollector.push({
533
535
  ...prep,
@@ -535,7 +537,8 @@ class Responder {
535
537
  data: {
536
538
  ...prep.data,
537
539
  ...data
538
- }
540
+ },
541
+ useCa
539
542
  });
540
543
  } else {
541
544
  this._quickReplyCollector.push({
@@ -543,6 +546,7 @@ class Responder {
543
546
  action: this.toAbsoluteAction(action),
544
547
  title,
545
548
  data,
549
+ useCa,
546
550
  ...prep
547
551
  });
548
552
  }
@@ -4,7 +4,7 @@
4
4
  'use strict';
5
5
 
6
6
  const ai = require('./Ai');
7
- const { FEATURE_PHRASES } = require('./features');
7
+ const { FEATURE_PHRASES, FEATURE_TRACKING } = require('./features');
8
8
  const { FLAG_DO_NOT_LOG } = require('./flags');
9
9
 
10
10
  /** @typedef {import('./Request')} Request */
@@ -70,8 +70,10 @@ class ReturnSender {
70
70
 
71
71
  this._sequence = 0;
72
72
 
73
- this._sendLastMessageWithFinish = Array.isArray(incommingMessage.features)
74
- && incommingMessage.features.includes(FEATURE_PHRASES);
73
+ this._features = Array.isArray(incommingMessage.features) ? incommingMessage.features : ['text'];
74
+
75
+ this._sendLastMessageWithFinish = this._features.includes(FEATURE_PHRASES)
76
+ || this._features.includes(FEATURE_TRACKING);
75
77
 
76
78
  /**
77
79
  * @type {Function}
@@ -196,9 +198,22 @@ class ReturnSender {
196
198
  ? this.confidentInputFilter
197
199
  : this.textFilter;
198
200
 
201
+ let { message } = payload;
202
+
203
+ if (message && message.voice && message.voice.ssml) {
204
+ message = {
205
+ ...message,
206
+ text: message.text ? message.text : message.voice.ssml,
207
+ voice: {
208
+ ...message.voice,
209
+ ssml: filter(message.voice.ssml)
210
+ }
211
+ };
212
+ }
213
+
199
214
  // text message
200
- if (payload.message && payload.message.text) {
201
- let { text } = payload.message;
215
+ if (message && message.text) {
216
+ let { text } = message;
202
217
 
203
218
  if (req && req._anonymizedText) {
204
219
  text = req._anonymizedText;
@@ -207,27 +222,27 @@ class ReturnSender {
207
222
  return {
208
223
  ...payload,
209
224
  message: {
210
- ...payload.message,
225
+ ...message,
211
226
  text: filter(text)
212
227
  }
213
228
  };
214
229
  }
215
230
 
216
231
  // button message
217
- if (payload.message && payload.message.attachment
218
- && payload.message.attachment.type === 'template'
219
- && payload.message.attachment.payload
220
- && payload.message.attachment.payload.text) {
232
+ if (message && message.attachment
233
+ && message.attachment.type === 'template'
234
+ && message.attachment.payload
235
+ && message.attachment.payload.text) {
221
236
 
222
237
  return {
223
238
  ...payload,
224
239
  message: {
225
- ...payload.message,
240
+ ...message,
226
241
  attachment: {
227
- ...payload.message.attachment,
242
+ ...message.attachment,
228
243
  payload: {
229
- ...payload.message.attachment.payload,
230
- text: filter(payload.message.attachment.payload.text)
244
+ ...message.attachment.payload,
245
+ text: filter(message.attachment.payload.text)
231
246
  }
232
247
  }
233
248
  }
@@ -246,9 +261,7 @@ class ReturnSender {
246
261
  payload = this._queue.shift();
247
262
 
248
263
  let lastInQueueForNow = this._queue.length === 0;
249
- if (this._queue.length === 0
250
- && (this._sendLastMessageWithFinish
251
- || this._intentsAndEntities.some((e) => e && e.type))) {
264
+ if (this._queue.length === 0 && this._sendLastMessageWithFinish) {
252
265
  await Promise.race([
253
266
  this._anotherEventPromise,
254
267
  this._finishedPromise
@@ -279,8 +292,16 @@ class ReturnSender {
279
292
  }
280
293
 
281
294
  async _enrichPayload (payload, req, lastInQueueForNow) {
282
- if (lastInQueueForNow && req && this._intentsAndEntities.length !== 0) {
283
- const { phrases } = this._sendLastMessageWithFinish
295
+ if (!lastInQueueForNow) {
296
+ return;
297
+ }
298
+ if (this._features.includes(FEATURE_TRACKING)) {
299
+ const tracking = this._createTracking(req);
300
+ Object.assign(payload, { tracking });
301
+ }
302
+ if (req && this._intentsAndEntities.length !== 0) {
303
+ const supportsPhrases = this._features.includes(FEATURE_PHRASES);
304
+ const { phrases } = supportsPhrases
284
305
  ? await ai.ai.getPhrases(req)
285
306
  : { phrases: new Map() };
286
307
 
@@ -295,7 +316,7 @@ class ReturnSender {
295
316
  input = aiObj;
296
317
  return;
297
318
  }
298
- if (!this._sendLastMessageWithFinish) {
319
+ if (!supportsPhrases) {
299
320
  return;
300
321
  }
301
322
  if (aiObj.startsWith('@')) {
@@ -349,6 +370,8 @@ class ReturnSender {
349
370
  }
350
371
  if (Array.isArray(payload.expectedIntentsAndEntities)) {
351
372
  this._intentsAndEntities.push(...payload.expectedIntentsAndEntities);
373
+ this._sendLastMessageWithFinish = this._sendLastMessageWithFinish
374
+ || this._intentsAndEntities.some((e) => e && e.type);
352
375
  return;
353
376
  }
354
377
 
@@ -403,6 +426,29 @@ class ReturnSender {
403
426
  };
404
427
  }
405
428
 
429
+ _createTracking (req = null, res = null) {
430
+ const payload = {};
431
+ const meta = {
432
+ actions: this._visitedInteractions.slice()
433
+ };
434
+
435
+ if (req) {
436
+ Object.assign(meta, {
437
+ intent: req.intent(ai.ai.confidence),
438
+ confidence: ai.ai.confidence,
439
+ intents: (req.intents || [])
440
+ .map((i) => this._cleanupIntent(i)),
441
+ entities: this._cleanupEntities((req.entities || [])
442
+ .filter((e) => e.score >= ai.ai.confidence))
443
+ });
444
+ }
445
+ if (res) {
446
+ Object.assign(payload, res.senderMeta);
447
+ }
448
+
449
+ return Object.assign(this._tracking, { payload, meta });
450
+ }
451
+
406
452
  /**
407
453
  * @private
408
454
  * @param {Request} req
package/src/Router.js CHANGED
@@ -271,7 +271,7 @@ class Router extends ReducerWrapper {
271
271
  }
272
272
 
273
273
  // used as protected method
274
- async processReducers (reducers, req, res, postBack, path, action, doNotTrack = false) {
274
+ async processReducers (reducers, req, res, postBack, action, doNotTrack = false) {
275
275
  const routeToReduce = {
276
276
  reducers,
277
277
  path: res.routePath
package/src/Tester.js CHANGED
@@ -69,9 +69,21 @@ class Tester {
69
69
  info: e => console.info(e) // eslint-disable-line
70
70
  };
71
71
 
72
+ this._cachedGiMap = null;
73
+
72
74
  this._listener = (senderIdentifier, action, text, req, prevAction, doNotTrack) => {
75
+ const reqAction = req.action();
76
+ if (reqAction
77
+ && !this._actionMatches(action, reqAction)
78
+ && this._actionHasGlobalIntent(reqAction)) {
79
+
80
+ this._actionsCollector.push({
81
+ action: reqAction, text, prevAction, doNotTrack, isReqAction: true
82
+ });
83
+ }
84
+
73
85
  this._actionsCollector.push({
74
- action, text, prevAction, doNotTrack
86
+ action, text, prevAction, doNotTrack, isReqAction: false
75
87
  });
76
88
  };
77
89
 
@@ -127,10 +139,27 @@ class Tester {
127
139
  this.features = null;
128
140
  }
129
141
 
142
+ _actionHasGlobalIntent (action) {
143
+ if (!this.processor.reducer
144
+ || !('globalIntents' in this.processor.reducer)) {
145
+ return false;
146
+ }
147
+ if (this._cachedGiMap === null) {
148
+ this._cachedGiMap = new Set();
149
+
150
+ for (const value of this.processor.reducer.globalIntents.values()) {
151
+ this._cachedGiMap.add(value.action);
152
+ }
153
+ }
154
+
155
+ return this._cachedGiMap.has(action.replace(/^\/?/, '/'));
156
+ }
157
+
130
158
  dealloc () {
131
159
  this.processor.reducer
132
160
  .removeListener('_action', this._listener);
133
161
  this.processor.reducer = null;
162
+ this._cachedGiMap = null;
134
163
  }
135
164
 
136
165
  /**
@@ -255,22 +284,30 @@ class Tester {
255
284
  return new ResponseAssert(this.responses[this.responses.length - 1]);
256
285
  }
257
286
 
287
+ _actionMatches (botAction, path) {
288
+ return botAction === path
289
+ || (path === '*' && botAction === '/*')
290
+ || (!botAction.match(/\*/) && actionMatches(botAction, path));
291
+ }
292
+
258
293
  /**
259
- * Checks, that app past the action
294
+ * Checks, that request passed an interaction
260
295
  *
261
296
  * @param {string} path
297
+ * @param {boolean} [matchRequestActions]
262
298
  * @returns {this}
263
299
  *
264
300
  * @memberOf Tester
265
301
  */
266
- passedAction (path) {
302
+ passedAction (path, matchRequestActions = false) {
267
303
  const ok = this.actions
268
- .some((action) => (action.action === path
269
- || (!action.action.match(/\*/) && actionMatches(action.action, path))));
304
+ .some((action) => (!action.isReqAction || matchRequestActions)
305
+ && this._actionMatches(action.action, path));
270
306
  let actual;
271
307
  if (!ok) {
272
308
  const set = new Set();
273
309
  actual = this.actions
310
+ .filter((a) => !a.isReqAction || matchRequestActions)
274
311
  .map((a) => (a.doNotTrack ? `(system interaction) ${a.action}` : a.action))
275
312
  .filter((a) => !set.has(a) && set.add(a));
276
313
  assert.fail(asserts.ex('Interaction was not passed', path, actual));
package/src/features.js CHANGED
@@ -23,6 +23,11 @@ const FEATURE_SSML = 'ssml';
23
23
  */
24
24
  const FEATURE_PHRASES = 'phrases';
25
25
 
26
+ /**
27
+ * @constant {string} FEATURE_TRACKING channel supports tracking protocol
28
+ */
29
+ const FEATURE_TRACKING = 'tracking';
30
+
26
31
  function getDefaultFeatureList () {
27
32
  return [FEATURE_TEXT];
28
33
  }
@@ -32,5 +37,6 @@ module.exports = {
32
37
  FEATURE_VOICE,
33
38
  FEATURE_SSML,
34
39
  FEATURE_PHRASES,
40
+ FEATURE_TRACKING,
35
41
  getDefaultFeatureList
36
42
  };
@@ -5,7 +5,16 @@
5
5
 
6
6
  const Router = require('../Router');
7
7
 
8
- function expected ({ path }, { isLastIndex }) {
8
+ function expected ({ path, attachedRouter }, { isLastIndex }) {
9
+
10
+ if (attachedRouter) {
11
+ return (req, res, postBack) => postBack(path, {
12
+ _useExpected: {
13
+ action: res.toAbsoluteAction(path),
14
+ data: {}
15
+ }
16
+ }, true);
17
+ }
9
18
 
10
19
  return (req, res) => {
11
20
  res.expected(path);
@@ -6,14 +6,6 @@
6
6
  const Router = require('../Router'); // eslint-disable-line
7
7
  const ai = require('../Ai'); // eslint-disable-line
8
8
  const fetch = require('node-fetch'); // eslint-disable-line
9
- let request;
10
- try {
11
- // @ts-ignore
12
- request = module.require('request-promise-native');
13
- } catch (e) {
14
- // eslint-disable-next-line no-unused-vars
15
- request = () => { throw new Error('To use request, you have to manually install request-promise-native into your bot.'); };
16
- }
17
9
  let axios;
18
10
  try {
19
11
  // @ts-ignore
@@ -20,7 +20,7 @@ const UNSUBSCRIBE = '_$unsubscribe';
20
20
 
21
21
  function getUpdate (attr, value, currentState = {}) {
22
22
  let param;
23
- let rest = attr;
23
+ let rest = attr && attr.replace(/\u2219/g, '.');
24
24
  let state = currentState;
25
25
  const ret = {};
26
26
  let up = ret;
@@ -53,7 +53,7 @@ function getUpdate (attr, value, currentState = {}) {
53
53
 
54
54
  function getValue (attr, currentState = {}) {
55
55
  let param;
56
- let rest = attr;
56
+ let rest = attr && attr.replace(/\u2219/g, '.');
57
57
  let state = currentState;
58
58
 
59
59
  do {
@@ -133,7 +133,8 @@ function makeQuickReplies (replies, path = '', translate = (w) => w, quickReplyC
133
133
  data = {},
134
134
  isLocation = false,
135
135
  isEmail = false,
136
- isPhone = false
136
+ isPhone = false,
137
+ useCa = currentAction
137
138
  } = reply;
138
139
  let {
139
140
  setState = null
@@ -219,7 +220,7 @@ function makeQuickReplies (replies, path = '', translate = (w) => w, quickReplyC
219
220
  payload = {
220
221
  action: absoluteAction,
221
222
  data: {
222
- _ca: currentAction,
223
+ _ca: useCa,
223
224
  ...data
224
225
  }
225
226
  };
@@ -41,7 +41,7 @@ function wrapPluginFunction (
41
41
  return true;
42
42
  }
43
43
  const reducers = preprocessedItems.get(codeBlockName);
44
- return router.processReducers(reducers, req, res, postBack, path, action, true);
44
+ return router.processReducers(reducers, req, res, postBack, action, true);
45
45
  }
46
46
  });
47
47