wingbot 3.32.0 → 3.33.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 +1 -1
- package/src/Ai.js +2 -2
- package/src/BuildRouter.js +320 -32
- package/src/ConversationTester.js +2 -8
- package/src/Processor.js +7 -1
- package/src/Request.js +3 -0
- package/src/Router.js +21 -7
- package/src/Tester.js +20 -5
- package/src/notifications/Notifications.js +1 -1
- package/src/resolvers/bounce.js +6 -3
- package/src/resolvers/button.js +16 -11
- package/src/resolvers/carousel.js +8 -10
- package/src/resolvers/media.js +13 -7
- package/src/resolvers/message.js +24 -10
- package/src/resolvers/postback.js +13 -3
- package/src/resolvers/setState.js +12 -3
- package/src/resolvers/subscribtions.js +13 -3
- package/src/resolvers/utils.js +6 -6
- package/src/utils/customCondition.js +3 -2
- package/src/utils/getCondition.js +15 -2
- package/src/utils/getUpdate.js +5 -5
- package/src/utils/stateData.js +5 -2
package/package.json
CHANGED
package/src/Ai.js
CHANGED
|
@@ -229,7 +229,7 @@ class Ai {
|
|
|
229
229
|
*
|
|
230
230
|
* @param {string} path
|
|
231
231
|
* @param {IntentRule|IntentRule[]} intents
|
|
232
|
-
* @param {string} [title] - disambiguation title
|
|
232
|
+
* @param {string|Function} [title] - disambiguation title
|
|
233
233
|
* @param {object} [meta] - metadata for multibot environments
|
|
234
234
|
* @param {object} [meta.targetAppId] - target application id
|
|
235
235
|
* @param {object} [meta.targetAction] - target action
|
|
@@ -276,7 +276,7 @@ class Ai {
|
|
|
276
276
|
*
|
|
277
277
|
* @param {string} path
|
|
278
278
|
* @param {IntentRule|IntentRule[]} intents
|
|
279
|
-
* @param {string} [title] - disambiguation title
|
|
279
|
+
* @param {string|Function} [title] - disambiguation title
|
|
280
280
|
* @returns {object} - the middleware
|
|
281
281
|
* @memberOf Ai
|
|
282
282
|
* @example
|
package/src/BuildRouter.js
CHANGED
|
@@ -17,6 +17,67 @@ const { shouldExecuteResolver } = require('./resolvers/resolverTags');
|
|
|
17
17
|
|
|
18
18
|
const MESSAGE_RESOLVER_NAME = 'botbuild.message';
|
|
19
19
|
|
|
20
|
+
/** @typedef {import('./Router').Middleware} Middleware */
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @typedef {object} Resolver
|
|
24
|
+
* @prop {string} type
|
|
25
|
+
* @prop {object} params
|
|
26
|
+
* @prop {string} [params.staticBlockId]
|
|
27
|
+
* @prop {string} [tag]
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
/** @typedef {import('./resolvers/bounce').BounceAllow} BounceAllow */
|
|
31
|
+
/** @typedef {import('./resolvers/bounce').BounceReturn} BounceReturn */
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @typedef {object} Route
|
|
35
|
+
* @prop {number} id
|
|
36
|
+
* @prop {string|null} path
|
|
37
|
+
* @prop {Resolver[]} resolvers
|
|
38
|
+
* @prop {boolean} [isFallback]
|
|
39
|
+
* @prop {string[]} [aiTags]
|
|
40
|
+
* @prop {boolean} [isResponder]
|
|
41
|
+
* @prop {number} [respondsToRouteId]
|
|
42
|
+
* @prop {string|any[]} [aiTitle]
|
|
43
|
+
* @prop {boolean} [aiGlobal]
|
|
44
|
+
* @prop {BounceAllow} [bounceAllowedTo]
|
|
45
|
+
* @prop {BounceReturn} [bounceReturn]
|
|
46
|
+
* @prop {boolean} [isEntryPoint]
|
|
47
|
+
*/
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @typedef {object} RouteTransformation
|
|
51
|
+
* @prop {string} [expectedPath]
|
|
52
|
+
*
|
|
53
|
+
* @typedef {RouteTransformation & Route} TransformedRoute
|
|
54
|
+
*/
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @typedef {Map<string|number,string>} LinksMap
|
|
58
|
+
*/
|
|
59
|
+
|
|
60
|
+
/** @type {TransformedRoute} */
|
|
61
|
+
const DUMMY_ROUTE = { id: 0, path: null, resolvers: [] };
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @typedef {object} Block
|
|
65
|
+
* @prop {string} staticBlockId
|
|
66
|
+
* @prop {Route[]} routes
|
|
67
|
+
* @prop {boolean} [isRoot]
|
|
68
|
+
* @prop {boolean} [disabled]
|
|
69
|
+
* @prop {string} [blockName]
|
|
70
|
+
* @prop {string} [blockType]
|
|
71
|
+
*/
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* @typedef {object} BotConfig
|
|
75
|
+
* @prop {string} botId - the ID of bot
|
|
76
|
+
* @prop {string} snapshot - snapshot stage of bot
|
|
77
|
+
* @prop {string|Promise<string>} token - authorization token for bot
|
|
78
|
+
* @prop {string} [url] - specify alternative configuration resource
|
|
79
|
+
*/
|
|
80
|
+
|
|
20
81
|
/**
|
|
21
82
|
* @typedef {object} ConfigStorage
|
|
22
83
|
* @prop {{():Promise}} invalidateConfig
|
|
@@ -25,6 +86,61 @@ const MESSAGE_RESOLVER_NAME = 'botbuild.message';
|
|
|
25
86
|
* @prop {{():Promise<Object>}} getConfig
|
|
26
87
|
*/
|
|
27
88
|
|
|
89
|
+
/**
|
|
90
|
+
* @typedef {object} RouteConfig
|
|
91
|
+
* @prop {string} path
|
|
92
|
+
* @prop {boolean} enabled
|
|
93
|
+
* @prop {object} configuration
|
|
94
|
+
*/
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* @callback LinkTranslator
|
|
98
|
+
* @param {string} senderId
|
|
99
|
+
* @param {string} textLabel
|
|
100
|
+
* @param {string} urlText
|
|
101
|
+
* @param {boolean} [isExtUrl]
|
|
102
|
+
* @param {object} [state]
|
|
103
|
+
* @returns {string}
|
|
104
|
+
*/
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* @typedef {object} BotContext
|
|
108
|
+
* @prop {LinkTranslator} [linksTranslator] - function, that translates links globally
|
|
109
|
+
* @prop {ConfigStorage} [configStorage] - function, that translates links globally
|
|
110
|
+
* @prop {boolean} [allowForbiddenSnippetWords] - disable security rule
|
|
111
|
+
* @prop {RouteConfig[]} [routeConfigs] - list of disabled routes
|
|
112
|
+
* @prop {object} [configuration] - context data
|
|
113
|
+
* @prop {LinksMap} [linksMap]
|
|
114
|
+
* @prop {boolean} [isLastIndex]
|
|
115
|
+
* @prop {boolean} [isLastMessage]
|
|
116
|
+
* @prop {BuildRouter} [router]
|
|
117
|
+
* @prop {string} [path]
|
|
118
|
+
* @prop {boolean} [isFallback]
|
|
119
|
+
* @prop {string} [expectedPath]
|
|
120
|
+
* @prop {boolean} [isResponder]
|
|
121
|
+
* @prop {string|number} [routeId]
|
|
122
|
+
* @prop {string} [blockName]
|
|
123
|
+
* @prop {string} [blockType]
|
|
124
|
+
* @prop {boolean} [isRoot]
|
|
125
|
+
* @prop {string} [staticBlockId]
|
|
126
|
+
* @prop {Block[]} [blocks]
|
|
127
|
+
* @prop {object} [BuildRouter]
|
|
128
|
+
*/
|
|
129
|
+
|
|
130
|
+
function deepFreeze (object) {
|
|
131
|
+
const propNames = Object.getOwnPropertyNames(object);
|
|
132
|
+
|
|
133
|
+
for (const name of propNames) {
|
|
134
|
+
const value = object[name];
|
|
135
|
+
|
|
136
|
+
if (value && typeof value === 'object') {
|
|
137
|
+
deepFreeze(value);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return Object.freeze(object);
|
|
142
|
+
}
|
|
143
|
+
|
|
28
144
|
/**
|
|
29
145
|
* Build bot from Wingbot configuration file or snapshot url
|
|
30
146
|
*
|
|
@@ -36,16 +152,14 @@ class BuildRouter extends Router {
|
|
|
36
152
|
* Create new router from configuration
|
|
37
153
|
*
|
|
38
154
|
* @constructor
|
|
39
|
-
* @param {
|
|
40
|
-
* @param {string} [block.botId] - the ID of bot
|
|
41
|
-
* @param {string} [block.snapshot] - snapshot stage of bot
|
|
42
|
-
* @param {string|Promise<string>} [block.token] - authorization token for bot
|
|
43
|
-
* @param {string} [block.url] - specify alternative configuration resource
|
|
155
|
+
* @param {BotConfig|Block} block
|
|
44
156
|
* @param {Plugins} plugins - custom code blocks resource
|
|
45
157
|
* @param {object} context - the building context
|
|
46
158
|
* @param {object} [context.linksTranslator] - function, that translates links globally
|
|
47
159
|
* @param {ConfigStorage} [context.configStorage] - function, that translates links globally
|
|
48
160
|
* @param {boolean} [context.allowForbiddenSnippetWords] - disable security rule
|
|
161
|
+
* @param {RouteConfig[]} [context.routeConfigs] - list of disabled routes
|
|
162
|
+
* @param {object} [context.configuration]
|
|
49
163
|
* @param {fetch} [fetchFn] - override a request function
|
|
50
164
|
* @example
|
|
51
165
|
*
|
|
@@ -76,8 +190,10 @@ class BuildRouter extends Router {
|
|
|
76
190
|
|
|
77
191
|
this._plugins = plugins;
|
|
78
192
|
|
|
193
|
+
/** @type {BotContext} */
|
|
79
194
|
this._context = context;
|
|
80
195
|
|
|
196
|
+
/** @type {LinksMap} */
|
|
81
197
|
this._linksMap = new Map();
|
|
82
198
|
|
|
83
199
|
this._loadBotUrl = null;
|
|
@@ -94,7 +210,7 @@ class BuildRouter extends Router {
|
|
|
94
210
|
|
|
95
211
|
this.resources = defaultResourceMap();
|
|
96
212
|
|
|
97
|
-
this._loadBotAuthorization = block.token
|
|
213
|
+
this._loadBotAuthorization = 'token' in block ? block.token : null;
|
|
98
214
|
|
|
99
215
|
this._configStorage = context.configStorage;
|
|
100
216
|
|
|
@@ -112,7 +228,7 @@ class BuildRouter extends Router {
|
|
|
112
228
|
this._snapshot = null;
|
|
113
229
|
this._botId = null;
|
|
114
230
|
|
|
115
|
-
if (
|
|
231
|
+
if ('routes' in block) {
|
|
116
232
|
this._buildBot(block);
|
|
117
233
|
} else if (typeof block.url === 'string') {
|
|
118
234
|
this._loadBotUrl = block.url;
|
|
@@ -315,6 +431,12 @@ class BuildRouter extends Router {
|
|
|
315
431
|
}
|
|
316
432
|
}
|
|
317
433
|
|
|
434
|
+
/**
|
|
435
|
+
*
|
|
436
|
+
* @param {Block} block
|
|
437
|
+
* @param {number} setConfigTimestamp
|
|
438
|
+
* @param {string} lastmod
|
|
439
|
+
*/
|
|
318
440
|
_buildBot (block, setConfigTimestamp = Number.MAX_SAFE_INTEGER, lastmod = '-') {
|
|
319
441
|
try {
|
|
320
442
|
if (setConfigTimestamp !== Number.MAX_SAFE_INTEGER) {
|
|
@@ -390,7 +512,13 @@ class BuildRouter extends Router {
|
|
|
390
512
|
});
|
|
391
513
|
}
|
|
392
514
|
|
|
515
|
+
/**
|
|
516
|
+
*
|
|
517
|
+
* @param {Block} block
|
|
518
|
+
* @returns {LinksMap}
|
|
519
|
+
*/
|
|
393
520
|
_createLinksMap (block) {
|
|
521
|
+
/** @type {LinksMap} */
|
|
394
522
|
const linksMap = new Map();
|
|
395
523
|
|
|
396
524
|
block.routes
|
|
@@ -402,35 +530,140 @@ class BuildRouter extends Router {
|
|
|
402
530
|
if (prevLinksMap) {
|
|
403
531
|
for (const [from, to] of prevLinksMap.entries()) {
|
|
404
532
|
if (!linksMap.has(from)) {
|
|
405
|
-
|
|
406
|
-
(path.posix || path).join('..', to);
|
|
407
|
-
|
|
408
|
-
linksMap.set(from, (path.posix || path).join('..', to));
|
|
533
|
+
linksMap.set(from, this._joinPaths('..', to));
|
|
409
534
|
}
|
|
410
535
|
}
|
|
411
536
|
}
|
|
412
537
|
|
|
413
538
|
block.routes.forEach((route) => {
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
539
|
+
const enabledNestedBlock = this._getBlockById(this._getIncludedBlockId(route));
|
|
540
|
+
if (!enabledNestedBlock) {
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
const routeConfig = this._getRouteConfig(route);
|
|
544
|
+
if (this._enabledByRouteConfig(routeConfig)) {
|
|
545
|
+
this._findEntryPointsInResolver(linksMap, enabledNestedBlock, route);
|
|
420
546
|
}
|
|
421
547
|
});
|
|
422
548
|
|
|
423
549
|
return linksMap;
|
|
424
550
|
}
|
|
425
551
|
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
552
|
+
/**
|
|
553
|
+
*
|
|
554
|
+
* @param {RouteConfig} routeConfig
|
|
555
|
+
*/
|
|
556
|
+
_enabledByRouteConfig (routeConfig) {
|
|
557
|
+
return !this._context.routeConfigs || (routeConfig && routeConfig.enabled);
|
|
558
|
+
}
|
|
429
559
|
|
|
430
|
-
|
|
431
|
-
|
|
560
|
+
_joinPaths (...args) {
|
|
561
|
+
return (path.posix || path).join(...args);
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
_normalizeConfigPath (routePath, ctxPath = null) {
|
|
565
|
+
const joined = ctxPath
|
|
566
|
+
? this._joinPaths(ctxPath, routePath)
|
|
567
|
+
: routePath;
|
|
568
|
+
|
|
569
|
+
return joined
|
|
570
|
+
.replace(/^\/?|(_responder)?\/?$/g, '/');
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
/**
|
|
574
|
+
*
|
|
575
|
+
* @param {Route} route
|
|
576
|
+
* @returns {string|null}
|
|
577
|
+
*/
|
|
578
|
+
_getIncludedBlockId (route) {
|
|
579
|
+
const includeResolver = route.resolvers.find((r) => r.type === 'botbuild.include');
|
|
580
|
+
|
|
581
|
+
return includeResolver
|
|
582
|
+
? includeResolver.params.staticBlockId
|
|
583
|
+
: null;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
*
|
|
588
|
+
* @param {string} staticBlockId
|
|
589
|
+
* @returns {Block|null}
|
|
590
|
+
*/
|
|
591
|
+
_getBlockById (staticBlockId) {
|
|
592
|
+
if (!staticBlockId) {
|
|
593
|
+
return null;
|
|
594
|
+
}
|
|
595
|
+
const nestedBlock = (this._context.blocks || [])
|
|
596
|
+
.find((b) => b.staticBlockId === staticBlockId);
|
|
597
|
+
|
|
598
|
+
if (!nestedBlock || nestedBlock.disabled) {
|
|
599
|
+
return null;
|
|
600
|
+
}
|
|
601
|
+
return nestedBlock;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
/**
|
|
605
|
+
*
|
|
606
|
+
* @param {TransformedRoute} route
|
|
607
|
+
* @returns {RouteConfig}
|
|
608
|
+
*/
|
|
609
|
+
_getRouteConfig (route) {
|
|
610
|
+
const { path: ctxPath, routeConfigs } = this._context;
|
|
611
|
+
if (!routeConfigs || !route.path || route.isFallback) {
|
|
612
|
+
return null;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
let rPath;
|
|
616
|
+
if (!route.isResponder) {
|
|
617
|
+
rPath = route.path;
|
|
618
|
+
} else if (route.expectedPath) {
|
|
619
|
+
rPath = route.expectedPath;
|
|
620
|
+
} else if (this._linksMap.has(route.respondsToRouteId)) {
|
|
621
|
+
rPath = this._linksMap.get(route.respondsToRouteId);
|
|
622
|
+
} else {
|
|
623
|
+
throw new Error('Illegal state');
|
|
432
624
|
}
|
|
433
625
|
|
|
626
|
+
const routePath = this._normalizeConfigPath(rPath, ctxPath);
|
|
627
|
+
return routeConfigs.find((config) => {
|
|
628
|
+
const configPath = this._normalizeConfigPath(config.path);
|
|
629
|
+
|
|
630
|
+
return configPath === routePath;
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
/**
|
|
635
|
+
*
|
|
636
|
+
* @param {string} routePath
|
|
637
|
+
* @returns {object}
|
|
638
|
+
*/
|
|
639
|
+
getConfiguration (routePath) {
|
|
640
|
+
const { path: ctxPath, routeConfigs, configuration = {} } = this._context;
|
|
641
|
+
if (!routeConfigs || !routePath) {
|
|
642
|
+
return deepFreeze(configuration);
|
|
643
|
+
}
|
|
644
|
+
const normalized = this._normalizeConfigPath(routePath, ctxPath);
|
|
645
|
+
|
|
646
|
+
const configs = routeConfigs
|
|
647
|
+
.filter((config) => {
|
|
648
|
+
const configPath = this._normalizeConfigPath(config.path);
|
|
649
|
+
return normalized.startsWith(configPath);
|
|
650
|
+
})
|
|
651
|
+
.sort((a, z) => a.path.length - z.path.length);
|
|
652
|
+
|
|
653
|
+
return deepFreeze(configs.reduce((cfg, config) => ({
|
|
654
|
+
...cfg,
|
|
655
|
+
...config.configuration
|
|
656
|
+
}), configuration));
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
/**
|
|
660
|
+
*
|
|
661
|
+
* @param {LinksMap} linksMap
|
|
662
|
+
* @param {Block} includedBlock
|
|
663
|
+
* @param {TransformedRoute} route
|
|
664
|
+
* @returns {void}
|
|
665
|
+
*/
|
|
666
|
+
_findEntryPointsInResolver (linksMap, includedBlock, route) {
|
|
434
667
|
let basePath = `${route.path}/`;
|
|
435
668
|
|
|
436
669
|
if (route.isFallback) {
|
|
@@ -438,14 +671,20 @@ class BuildRouter extends Router {
|
|
|
438
671
|
}
|
|
439
672
|
|
|
440
673
|
includedBlock.routes.forEach((blockRoute) => {
|
|
441
|
-
if (!blockRoute.isEntryPoint
|
|
674
|
+
if (!blockRoute.isEntryPoint) {
|
|
442
675
|
return;
|
|
443
676
|
}
|
|
444
677
|
|
|
445
|
-
linksMap.set(
|
|
678
|
+
linksMap.set(blockRoute.id, `${basePath}${blockRoute.path}`);
|
|
446
679
|
});
|
|
447
680
|
}
|
|
448
681
|
|
|
682
|
+
/**
|
|
683
|
+
*
|
|
684
|
+
* @param {TransformedRoute} route
|
|
685
|
+
* @param {boolean} nextRouteIsSameResponder
|
|
686
|
+
* @returns {Middleware[]}
|
|
687
|
+
*/
|
|
449
688
|
_buildRouteHead (route, nextRouteIsSameResponder) {
|
|
450
689
|
const resolvers = [];
|
|
451
690
|
|
|
@@ -453,10 +692,10 @@ class BuildRouter extends Router {
|
|
|
453
692
|
let aiResolver = null;
|
|
454
693
|
|
|
455
694
|
if (route.aiTags && route.aiTags.length) {
|
|
456
|
-
let
|
|
695
|
+
let aiTitle = null;
|
|
457
696
|
|
|
458
|
-
if (aiTitle) {
|
|
459
|
-
aiTitle = cachedTranslatedCompilator(aiTitle);
|
|
697
|
+
if (route.aiTitle) {
|
|
698
|
+
aiTitle = cachedTranslatedCompilator(route.aiTitle);
|
|
460
699
|
}
|
|
461
700
|
|
|
462
701
|
if (route.aiGlobal) {
|
|
@@ -488,8 +727,25 @@ class BuildRouter extends Router {
|
|
|
488
727
|
return resolvers;
|
|
489
728
|
}
|
|
490
729
|
|
|
730
|
+
/**
|
|
731
|
+
*
|
|
732
|
+
* @param {TransformedRoute[]} routes
|
|
733
|
+
*/
|
|
491
734
|
_buildRoutes (routes) {
|
|
492
735
|
routes.forEach((route, i) => {
|
|
736
|
+
const routeConfig = this._getRouteConfig(route);
|
|
737
|
+
|
|
738
|
+
if (routeConfig && !routeConfig.enabled) {
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
const includedBlockId = this._getIncludedBlockId(route);
|
|
743
|
+
const nestedBlock = this._getBlockById(includedBlockId);
|
|
744
|
+
|
|
745
|
+
if (includedBlockId && (!nestedBlock || !this._enabledByRouteConfig(routeConfig))) {
|
|
746
|
+
return;
|
|
747
|
+
}
|
|
748
|
+
|
|
493
749
|
const nextRoute = routes.length > (i + 1)
|
|
494
750
|
? routes[i + 1]
|
|
495
751
|
: null;
|
|
@@ -523,6 +779,11 @@ class BuildRouter extends Router {
|
|
|
523
779
|
});
|
|
524
780
|
}
|
|
525
781
|
|
|
782
|
+
/**
|
|
783
|
+
*
|
|
784
|
+
* @param {Resolver[]} resolvers
|
|
785
|
+
* @returns {number}
|
|
786
|
+
*/
|
|
526
787
|
_lastMessageIndex (resolvers) {
|
|
527
788
|
for (let i = resolvers.length - 1; i >= 0; i--) {
|
|
528
789
|
if (resolvers[i].type === MESSAGE_RESOLVER_NAME) {
|
|
@@ -532,15 +793,30 @@ class BuildRouter extends Router {
|
|
|
532
793
|
return -1;
|
|
533
794
|
}
|
|
534
795
|
|
|
535
|
-
|
|
796
|
+
/**
|
|
797
|
+
*
|
|
798
|
+
* @param {Resolver[]} resolvers
|
|
799
|
+
* @param {TransformedRoute} route
|
|
800
|
+
* @param {*} buildInfo
|
|
801
|
+
* @returns {Middleware[]}
|
|
802
|
+
*/
|
|
803
|
+
buildResolvers (resolvers, route = DUMMY_ROUTE, buildInfo = {}) {
|
|
536
804
|
const {
|
|
537
805
|
path: ctxPath, isFallback, isResponder, expectedPath, id
|
|
538
806
|
} = route;
|
|
539
807
|
|
|
808
|
+
const routeConfig = this._getRouteConfig(route);
|
|
809
|
+
const routeConfigData = routeConfig && routeConfig.configuration;
|
|
810
|
+
|
|
540
811
|
const lastMessageIndex = this._lastMessageIndex(resolvers);
|
|
541
812
|
const lastIndex = resolvers.length - 1;
|
|
542
813
|
|
|
543
814
|
return resolvers.map((resolver, i) => {
|
|
815
|
+
const configuration = deepFreeze({
|
|
816
|
+
...this._context.configuration,
|
|
817
|
+
...routeConfigData
|
|
818
|
+
});
|
|
819
|
+
|
|
544
820
|
const context = {
|
|
545
821
|
...this._context,
|
|
546
822
|
isLastIndex: lastIndex === i && !buildInfo.expectedToAddResolver,
|
|
@@ -551,13 +827,25 @@ class BuildRouter extends Router {
|
|
|
551
827
|
isFallback,
|
|
552
828
|
isResponder,
|
|
553
829
|
expectedPath,
|
|
554
|
-
routeId: id
|
|
830
|
+
routeId: id,
|
|
831
|
+
configuration
|
|
555
832
|
};
|
|
556
833
|
|
|
557
|
-
|
|
834
|
+
const resFn = this._resolverFactory(resolver, context, buildInfo);
|
|
835
|
+
|
|
836
|
+
// @ts-ignore
|
|
837
|
+
resFn.configuration = configuration;
|
|
838
|
+
return resFn;
|
|
558
839
|
});
|
|
559
840
|
}
|
|
560
841
|
|
|
842
|
+
/**
|
|
843
|
+
*
|
|
844
|
+
* @param {Resolver} resolver
|
|
845
|
+
* @param {BotContext} context
|
|
846
|
+
* @param {*} buildInfo
|
|
847
|
+
* @returns {Middleware}
|
|
848
|
+
*/
|
|
561
849
|
_resolverFactory (resolver, context, buildInfo) {
|
|
562
850
|
const { type } = resolver;
|
|
563
851
|
|
|
@@ -618,9 +906,9 @@ class BuildRouter extends Router {
|
|
|
618
906
|
}
|
|
619
907
|
|
|
620
908
|
/**
|
|
621
|
-
* @param {
|
|
909
|
+
* @param {Block[]} blocks - blocks list
|
|
622
910
|
* @param {Plugins} [plugins]
|
|
623
|
-
* @param {
|
|
911
|
+
* @param {BotContext} [context]
|
|
624
912
|
*/
|
|
625
913
|
BuildRouter.fromData = function fromData (blocks, plugins = new Plugins(), context = {}) {
|
|
626
914
|
|
|
@@ -603,19 +603,13 @@ class ConversationTester {
|
|
|
603
603
|
} else {
|
|
604
604
|
const quickReplyRequired = action.match(/^>/);
|
|
605
605
|
const cleanAction = action.replace(/^>/, '');
|
|
606
|
-
let found;
|
|
607
606
|
|
|
608
607
|
// action in quick reply
|
|
609
608
|
if (action.match(/^>\//)) {
|
|
610
609
|
await t.quickReply(cleanAction);
|
|
611
|
-
|
|
610
|
+
} else if (quickReplyRequired) {
|
|
611
|
+
await t.quickReplyText(cleanAction);
|
|
612
612
|
} else {
|
|
613
|
-
found = await t.quickReplyText(cleanAction);
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
if (!found && quickReplyRequired) {
|
|
617
|
-
throw new Error(`Quick reply "${action.replace(/^>/, '')}" was required, but has not been found`);
|
|
618
|
-
} else if (!found) {
|
|
619
613
|
await t.text(action);
|
|
620
614
|
}
|
|
621
615
|
}
|
package/src/Processor.js
CHANGED
|
@@ -14,6 +14,7 @@ const { mergeState } = require('./utils/stateVariables');
|
|
|
14
14
|
/** @typedef {import('./wingbot/CustomEntityDetectionModel').Intent} Intent */
|
|
15
15
|
/** @typedef {import('./ReducerWrapper')} ReducerWrapper */
|
|
16
16
|
/** @typedef {import('./Router')} Router */
|
|
17
|
+
/** @typedef {import('./BuildRouter')} BuildRouter */
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* @typedef {object} AutoTypingConfig
|
|
@@ -121,7 +122,7 @@ class Processor extends EventEmitter {
|
|
|
121
122
|
/**
|
|
122
123
|
* Creates an instance of Processor
|
|
123
124
|
*
|
|
124
|
-
* @param {ReducerWrapper|Router} reducer
|
|
125
|
+
* @param {ReducerWrapper|Router|BuildRouter} reducer
|
|
125
126
|
* @param {ProcessorOptions} [options] - processor options
|
|
126
127
|
*
|
|
127
128
|
* @memberOf Processor
|
|
@@ -548,6 +549,11 @@ class Processor extends EventEmitter {
|
|
|
548
549
|
res.setBookmark(aByAi);
|
|
549
550
|
}
|
|
550
551
|
|
|
552
|
+
if ('getConfiguration' in this.reducer) {
|
|
553
|
+
const configuration = this.reducer.getConfiguration(state._lastAction);
|
|
554
|
+
req.configuration = Object.freeze(configuration);
|
|
555
|
+
}
|
|
556
|
+
|
|
551
557
|
// process setState
|
|
552
558
|
const setState = req.getSetState(req.AI_SETSTATE.EXCLUDE_WITH_SET_ENTITIES);
|
|
553
559
|
await Ai.ai.processSetStateEntities(req, setState);
|
package/src/Request.js
CHANGED
package/src/Router.js
CHANGED
|
@@ -140,7 +140,12 @@ class Router extends ReducerWrapper {
|
|
|
140
140
|
|
|
141
141
|
const reducersArray = reducer.map((re) => {
|
|
142
142
|
const {
|
|
143
|
-
resolverPath,
|
|
143
|
+
resolverPath,
|
|
144
|
+
reduce,
|
|
145
|
+
isReducer,
|
|
146
|
+
globalIntents: gis,
|
|
147
|
+
globalIntentsMeta,
|
|
148
|
+
configuration
|
|
144
149
|
} = this._createReducer(
|
|
145
150
|
re,
|
|
146
151
|
pathContext.path
|
|
@@ -149,16 +154,19 @@ class Router extends ReducerWrapper {
|
|
|
149
154
|
Object.assign(pathContext, { path: resolverPath });
|
|
150
155
|
Object.assign(pathContext.globalIntentsMeta, globalIntentsMeta);
|
|
151
156
|
isAnyReducer = isAnyReducer || isReducer;
|
|
152
|
-
return { reduce, isReducer };
|
|
157
|
+
return { reduce, isReducer, configuration };
|
|
153
158
|
});
|
|
154
159
|
|
|
155
160
|
return {
|
|
156
|
-
reducers: reducersArray,
|
|
161
|
+
reducers: reducersArray,
|
|
162
|
+
isReducer: isAnyReducer,
|
|
163
|
+
isOr: true,
|
|
164
|
+
globalIntents
|
|
157
165
|
};
|
|
158
166
|
}
|
|
159
167
|
|
|
160
168
|
const {
|
|
161
|
-
resolverPath, reduce, isReducer, globalIntents, globalIntentsMeta
|
|
169
|
+
resolverPath, reduce, isReducer, globalIntents, globalIntentsMeta, configuration
|
|
162
170
|
} = this._createReducer(
|
|
163
171
|
reducer,
|
|
164
172
|
pathContext.path
|
|
@@ -167,7 +175,9 @@ class Router extends ReducerWrapper {
|
|
|
167
175
|
Object.assign(pathContext, { path: resolverPath });
|
|
168
176
|
Object.assign(pathContext.globalIntentsMeta, globalIntentsMeta);
|
|
169
177
|
|
|
170
|
-
return {
|
|
178
|
+
return {
|
|
179
|
+
reduce, isReducer, globalIntents, configuration
|
|
180
|
+
};
|
|
171
181
|
});
|
|
172
182
|
}
|
|
173
183
|
|
|
@@ -178,7 +188,8 @@ class Router extends ReducerWrapper {
|
|
|
178
188
|
const {
|
|
179
189
|
globalIntents = new Map(),
|
|
180
190
|
path,
|
|
181
|
-
globalIntentsMeta = {}
|
|
191
|
+
globalIntentsMeta = {},
|
|
192
|
+
configuration = null
|
|
182
193
|
} = reducer;
|
|
183
194
|
|
|
184
195
|
if (typeof reducer === 'string' || path) {
|
|
@@ -219,7 +230,7 @@ class Router extends ReducerWrapper {
|
|
|
219
230
|
}
|
|
220
231
|
|
|
221
232
|
return {
|
|
222
|
-
resolverPath, isReducer, reduce, globalIntents, globalIntentsMeta
|
|
233
|
+
resolverPath, isReducer, reduce, globalIntents, globalIntentsMeta, configuration
|
|
223
234
|
};
|
|
224
235
|
}
|
|
225
236
|
|
|
@@ -291,6 +302,9 @@ class Router extends ReducerWrapper {
|
|
|
291
302
|
|
|
292
303
|
let pathContext = `${path === '/' ? '' : path}${route.path.replace(/\/\*/, '')}`;
|
|
293
304
|
res.setPath(path, route.path);
|
|
305
|
+
if (reducer.configuration) {
|
|
306
|
+
req.configuration = reducer.configuration;
|
|
307
|
+
}
|
|
294
308
|
|
|
295
309
|
let result;
|
|
296
310
|
|
package/src/Tester.js
CHANGED
|
@@ -452,7 +452,7 @@ class Tester {
|
|
|
452
452
|
}
|
|
453
453
|
|
|
454
454
|
/**
|
|
455
|
-
* Send quick reply if text exactly matches, otherwise
|
|
455
|
+
* Send quick reply if text exactly matches, otherwise throws exception
|
|
456
456
|
*
|
|
457
457
|
* @param {string} text
|
|
458
458
|
* @returns {Promise<boolean>}
|
|
@@ -460,22 +460,37 @@ class Tester {
|
|
|
460
460
|
* @memberOf Tester
|
|
461
461
|
*/
|
|
462
462
|
async quickReplyText (text) {
|
|
463
|
+
let found = '';
|
|
464
|
+
let but = 'has not been found';
|
|
463
465
|
|
|
464
466
|
if (this.responses.length !== 0) {
|
|
465
|
-
const
|
|
467
|
+
const normalize = (t) => `${t}`.toLocaleLowerCase().replace(/\s+/g, ' ').trim();
|
|
468
|
+
const normalizedText = normalize(text);
|
|
469
|
+
const search = tokenize(normalizedText);
|
|
466
470
|
const last = this.responses[this.responses.length - 1];
|
|
467
471
|
const quickReplys = asserts.getQuickReplies(last);
|
|
468
|
-
|
|
472
|
+
let res = quickReplys
|
|
469
473
|
.filter(({ title = '', payload }) => title && payload && tokenize(title) === search);
|
|
470
474
|
|
|
471
|
-
if (res
|
|
475
|
+
if (res.length > 1) {
|
|
476
|
+
res = res
|
|
477
|
+
.filter(({ title = '' }) => normalize(title) === normalizedText);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
if (res.length === 1) {
|
|
472
481
|
const { title, payload } = res[0];
|
|
473
482
|
await this.processMessage(Request.quickReplyText(this.senderId, title, payload));
|
|
474
483
|
return true;
|
|
475
484
|
}
|
|
485
|
+
|
|
486
|
+
if (res.length > 1) {
|
|
487
|
+
but = 'found multiple occurences';
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
found = ` (found: ${quickReplys.map((q) => q.title).filter((q) => !!q).join(', ')})`;
|
|
476
491
|
}
|
|
477
492
|
|
|
478
|
-
|
|
493
|
+
throw new Error(`Quick reply "${text}" was required, but ${but}.${found}`);
|
|
479
494
|
}
|
|
480
495
|
|
|
481
496
|
/**
|
|
@@ -545,7 +545,7 @@ class Notifications extends EventEmitter {
|
|
|
545
545
|
if (!campaign.hasEditableCondition) {
|
|
546
546
|
fn = customFn(campaign.condition, `Campaign "${campaign.name}" condition`);
|
|
547
547
|
} else {
|
|
548
|
-
fn = customCondition(campaign.editableCondition, `Campaign "${campaign.name}" condition`);
|
|
548
|
+
fn = customCondition(campaign.editableCondition, req.configuration, `Campaign "${campaign.name}" condition`);
|
|
549
549
|
}
|
|
550
550
|
|
|
551
551
|
const fnRes = fn(req, res);
|
package/src/resolvers/bounce.js
CHANGED
|
@@ -22,12 +22,15 @@ const BOUNCE_RETURN = {
|
|
|
22
22
|
IF_POSSIBLE: 'ifpos'
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
+
/** @typedef {import('../BuildRouter').Route} Route */
|
|
26
|
+
|
|
27
|
+
/** @typedef {BOUNCE_RETURN} BounceReturn */
|
|
28
|
+
/** @typedef {BOUNCE_ALLOW} BounceAllow */
|
|
29
|
+
|
|
25
30
|
/**
|
|
26
31
|
*
|
|
27
32
|
*
|
|
28
|
-
* @param {
|
|
29
|
-
* @param {BOUNCE_ALLOW} [route.bounceAllowedTo]
|
|
30
|
-
* @param {BOUNCE_RETURN} [route.bounceReturn]
|
|
33
|
+
* @param {Route} route
|
|
31
34
|
* @param {boolean} nextRouteIsSameResponder
|
|
32
35
|
* @param {string} [referredRoutePath]
|
|
33
36
|
* @returns {Function|null}
|
package/src/resolvers/button.js
CHANGED
|
@@ -11,19 +11,26 @@ const {
|
|
|
11
11
|
processButtons
|
|
12
12
|
} = require('./utils');
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
14
|
+
/** @typedef {import('../BuildRouter').BotContext} BotContext */
|
|
15
|
+
/** @typedef {import('../Router').Resolver} Resolver */
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
*
|
|
19
|
+
* @param {object} params
|
|
20
|
+
* @param {BotContext} context
|
|
21
|
+
* @returns {Resolver}
|
|
22
|
+
*/
|
|
23
|
+
function button (params, context) {
|
|
24
|
+
const {
|
|
25
|
+
isLastIndex
|
|
26
|
+
} = context;
|
|
20
27
|
const {
|
|
21
28
|
buttons = [],
|
|
22
29
|
text = null
|
|
23
30
|
} = params;
|
|
24
31
|
const compiledText = cachedTranslatedCompilator(text);
|
|
25
32
|
|
|
26
|
-
const condition = getCondition(params, ''
|
|
33
|
+
const condition = getCondition(params, context, 'button');
|
|
27
34
|
|
|
28
35
|
const ret = isLastIndex ? Router.END : Router.CONTINUE;
|
|
29
36
|
|
|
@@ -37,17 +44,15 @@ function button (params, {
|
|
|
37
44
|
}
|
|
38
45
|
}
|
|
39
46
|
|
|
40
|
-
const state = stateData(req, res);
|
|
47
|
+
const state = stateData(req, res, context.configuration);
|
|
41
48
|
const tpl = res.button(compiledText(state));
|
|
42
49
|
|
|
43
50
|
processButtons(
|
|
44
51
|
buttons,
|
|
45
52
|
state,
|
|
46
53
|
tpl,
|
|
47
|
-
linksMap,
|
|
48
54
|
req.senderId,
|
|
49
|
-
|
|
50
|
-
allowForbiddenSnippetWords,
|
|
55
|
+
context,
|
|
51
56
|
req,
|
|
52
57
|
res
|
|
53
58
|
);
|
|
@@ -17,12 +17,12 @@ const {
|
|
|
17
17
|
TYPE_URL_WITH_EXT
|
|
18
18
|
} = require('./utils');
|
|
19
19
|
|
|
20
|
-
function carousel (params, {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
20
|
+
function carousel (params, context) {
|
|
21
|
+
const {
|
|
22
|
+
isLastIndex,
|
|
23
|
+
linksMap,
|
|
24
|
+
linksTranslator = (a, b, c) => c
|
|
25
|
+
} = context;
|
|
26
26
|
const {
|
|
27
27
|
items = [],
|
|
28
28
|
shareable = false,
|
|
@@ -36,7 +36,7 @@ function carousel (params, {
|
|
|
36
36
|
return ret;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
const state = stateData(req, res);
|
|
39
|
+
const state = stateData(req, res, context.configuration);
|
|
40
40
|
const isSquare = imageAspect === ASPECT_SQUARE;
|
|
41
41
|
const tpl = res.genericTemplate(shareable, isSquare);
|
|
42
42
|
|
|
@@ -99,10 +99,8 @@ function carousel (params, {
|
|
|
99
99
|
buttons,
|
|
100
100
|
state,
|
|
101
101
|
elem,
|
|
102
|
-
linksMap,
|
|
103
102
|
senderId,
|
|
104
|
-
|
|
105
|
-
allowForbiddenSnippetWords,
|
|
103
|
+
context,
|
|
106
104
|
req,
|
|
107
105
|
res
|
|
108
106
|
);
|
package/src/resolvers/media.js
CHANGED
|
@@ -7,11 +7,17 @@ const Router = require('../Router');
|
|
|
7
7
|
const getCondition = require('../utils/getCondition');
|
|
8
8
|
const { stateData, cachedTranslatedCompilator } = require('./utils');
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
/** @typedef {import('../BuildRouter').BotContext} BotContext */
|
|
11
|
+
/** @typedef {import('../Router').Resolver} Resolver */
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
*
|
|
15
|
+
* @param {object} params
|
|
16
|
+
* @param {BotContext} context
|
|
17
|
+
* @returns {Resolver}
|
|
18
|
+
*/
|
|
19
|
+
function media (params, context = {}) {
|
|
20
|
+
const { isLastIndex } = context;
|
|
15
21
|
const { type, url } = params;
|
|
16
22
|
|
|
17
23
|
const urlString = url || '';
|
|
@@ -24,14 +30,14 @@ function media (
|
|
|
24
30
|
throw new Error(`Unsupported media type: ${type}`);
|
|
25
31
|
}
|
|
26
32
|
|
|
27
|
-
const condition = getCondition(params, 'Media condition');
|
|
33
|
+
const condition = getCondition(params, context, 'Media condition');
|
|
28
34
|
|
|
29
35
|
return (req, res) => {
|
|
30
36
|
if (condition && !condition(req, res)) {
|
|
31
37
|
return ret;
|
|
32
38
|
}
|
|
33
39
|
|
|
34
|
-
const data = stateData(req, res);
|
|
40
|
+
const data = stateData(req, res, context.configuration);
|
|
35
41
|
const sendUrl = urlTemplate(data);
|
|
36
42
|
|
|
37
43
|
res[type](sendUrl, true);
|
package/src/resolvers/message.js
CHANGED
|
@@ -44,10 +44,10 @@ function getVoiceControlFromParams (params, lang = null) {
|
|
|
44
44
|
return Object.keys(voiceControl).length > 0 ? voiceControl : null;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
function parseReplies (replies, linksMap,
|
|
47
|
+
function parseReplies (replies, linksMap, context) {
|
|
48
48
|
return replies.map((reply) => {
|
|
49
49
|
|
|
50
|
-
const condition = getCondition(reply, 'Quick reply condition'
|
|
50
|
+
const condition = getCondition(reply, context, 'Quick reply condition');
|
|
51
51
|
|
|
52
52
|
if (reply.isLocation) {
|
|
53
53
|
return {
|
|
@@ -78,6 +78,10 @@ function parseReplies (replies, linksMap, allowForbiddenSnippetWords) {
|
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
+
if (!action) {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
|
|
81
85
|
const ret = {
|
|
82
86
|
action,
|
|
83
87
|
condition,
|
|
@@ -104,7 +108,8 @@ function parseReplies (replies, linksMap, allowForbiddenSnippetWords) {
|
|
|
104
108
|
}
|
|
105
109
|
|
|
106
110
|
return ret;
|
|
107
|
-
})
|
|
111
|
+
})
|
|
112
|
+
.filter((r) => r !== null);
|
|
108
113
|
}
|
|
109
114
|
|
|
110
115
|
/**
|
|
@@ -154,11 +159,20 @@ function findSupportedMessages (text, features, lang = null) {
|
|
|
154
159
|
};
|
|
155
160
|
}
|
|
156
161
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
isLastIndex, isLastMessage, linksMap, allowForbiddenSnippetWords
|
|
160
|
-
} = {}) {
|
|
162
|
+
/** @typedef {import('../BuildRouter').BotContext} BotContext */
|
|
163
|
+
/** @typedef {import('../Router').Resolver} Resolver */
|
|
161
164
|
|
|
165
|
+
/**
|
|
166
|
+
*
|
|
167
|
+
* @param {object} params
|
|
168
|
+
* @param {BotContext} context
|
|
169
|
+
* @returns {Resolver}
|
|
170
|
+
*/
|
|
171
|
+
function message (params, context = {}) {
|
|
172
|
+
const {
|
|
173
|
+
// @ts-ignore
|
|
174
|
+
isLastIndex, isLastMessage, linksMap, configuration
|
|
175
|
+
} = context;
|
|
162
176
|
if (typeof params.text !== 'string' && !Array.isArray(params.text)) {
|
|
163
177
|
throw new Error('Message should be a text!');
|
|
164
178
|
}
|
|
@@ -168,11 +182,11 @@ function message (params, {
|
|
|
168
182
|
if (params.replies && !Array.isArray(params.replies)) {
|
|
169
183
|
throw new Error('Replies should be an array');
|
|
170
184
|
} else if (params.replies && params.replies.length > 0) {
|
|
171
|
-
quickReplies = parseReplies(params.replies, linksMap,
|
|
185
|
+
quickReplies = parseReplies(params.replies, linksMap, context);
|
|
172
186
|
}
|
|
173
187
|
|
|
174
188
|
// compile condition
|
|
175
|
-
const condition = getCondition(params, 'Message condition'
|
|
189
|
+
const condition = getCondition(params, context, 'Message condition');
|
|
176
190
|
|
|
177
191
|
const ret = isLastIndex ? Router.END : Router.CONTINUE;
|
|
178
192
|
|
|
@@ -184,7 +198,7 @@ function message (params, {
|
|
|
184
198
|
if (condition && !condition(req, res)) {
|
|
185
199
|
return ret;
|
|
186
200
|
}
|
|
187
|
-
const data = stateData(req, res);
|
|
201
|
+
const data = stateData(req, res, configuration);
|
|
188
202
|
|
|
189
203
|
// filter supported messages
|
|
190
204
|
const supportedText = findSupportedMessages(
|
|
@@ -7,7 +7,17 @@ const Router = require('../Router');
|
|
|
7
7
|
const getCondition = require('../utils/getCondition');
|
|
8
8
|
const { shouldExecuteResolver } = require('./resolverTags');
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
/** @typedef {import('../BuildRouter').BotContext} BotContext */
|
|
11
|
+
/** @typedef {import('../Router').Resolver} Resolver */
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
*
|
|
15
|
+
* @param {object} params
|
|
16
|
+
* @param {BotContext} context
|
|
17
|
+
* @returns {Resolver}
|
|
18
|
+
*/
|
|
19
|
+
function postback (params, context) {
|
|
20
|
+
const { linksMap, isLastIndex } = context;
|
|
11
21
|
const {
|
|
12
22
|
routeId,
|
|
13
23
|
postBack: staticAction
|
|
@@ -22,12 +32,12 @@ function postback (params, { linksMap, isLastIndex, allowForbiddenSnippetWords }
|
|
|
22
32
|
}
|
|
23
33
|
}
|
|
24
34
|
|
|
25
|
-
const condition = getCondition(params, ''
|
|
35
|
+
const condition = getCondition(params, context, 'postback');
|
|
26
36
|
|
|
27
37
|
const ret = isLastIndex ? Router.END : Router.CONTINUE;
|
|
28
38
|
|
|
29
39
|
return (req, res, postBack) => {
|
|
30
|
-
if (!shouldExecuteResolver(req, params)) {
|
|
40
|
+
if (!action || !shouldExecuteResolver(req, params)) {
|
|
31
41
|
return ret;
|
|
32
42
|
}
|
|
33
43
|
|
|
@@ -8,11 +8,20 @@ const Ai = require('../Ai');
|
|
|
8
8
|
const { getSetState } = require('../utils/getUpdate');
|
|
9
9
|
const getCondition = require('../utils/getCondition');
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
/** @typedef {import('../BuildRouter').BotContext} BotContext */
|
|
12
|
+
/** @typedef {import('../Router').Resolver} Resolver */
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
*
|
|
16
|
+
* @param {object} params
|
|
17
|
+
* @param {BotContext} context
|
|
18
|
+
* @returns {Resolver}
|
|
19
|
+
*/
|
|
20
|
+
function setState (params, context) {
|
|
12
21
|
|
|
13
|
-
const condition = getCondition(params, ''
|
|
22
|
+
const condition = getCondition(params, context, 'setState');
|
|
14
23
|
|
|
15
|
-
const ret = isLastIndex ? Router.END : Router.CONTINUE;
|
|
24
|
+
const ret = context.isLastIndex ? Router.END : Router.CONTINUE;
|
|
16
25
|
|
|
17
26
|
return async (req, res) => {
|
|
18
27
|
if (condition !== null) {
|
|
@@ -6,15 +6,24 @@
|
|
|
6
6
|
const Router = require('../Router');
|
|
7
7
|
const getCondition = require('../utils/getCondition');
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
/** @typedef {import('../BuildRouter').BotContext} BotContext */
|
|
10
|
+
/** @typedef {import('../Router').Resolver} Resolver */
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
*
|
|
14
|
+
* @param {object} params
|
|
15
|
+
* @param {BotContext} context
|
|
16
|
+
* @returns {Resolver}
|
|
17
|
+
*/
|
|
18
|
+
function subscribtions (params, context) {
|
|
10
19
|
const {
|
|
11
20
|
tags = [],
|
|
12
21
|
unsetTag = false
|
|
13
22
|
} = params;
|
|
14
23
|
|
|
15
|
-
const condition = getCondition(params, ''
|
|
24
|
+
const condition = getCondition(params, context, 'subscribtions');
|
|
16
25
|
|
|
17
|
-
const ret = isLastIndex ? Router.END : Router.CONTINUE;
|
|
26
|
+
const ret = context.isLastIndex ? Router.END : Router.CONTINUE;
|
|
18
27
|
const method = unsetTag ? 'unsubscribe' : 'subscribe';
|
|
19
28
|
|
|
20
29
|
return async (req, res) => {
|
|
@@ -35,6 +44,7 @@ function subscribtions (params, { isLastIndex, allowForbiddenSnippetWords }) {
|
|
|
35
44
|
}
|
|
36
45
|
|
|
37
46
|
if (tags.length === 0 && unsetTag) {
|
|
47
|
+
// @ts-ignore
|
|
38
48
|
res.unsubscribe();
|
|
39
49
|
}
|
|
40
50
|
|
package/src/resolvers/utils.js
CHANGED
|
@@ -205,18 +205,18 @@ function getText (text, state) {
|
|
|
205
205
|
// eslint-disable-next-line no-unused-vars
|
|
206
206
|
const DEFAULT_LINK_TRANSLATOR = (senderId, defaultText, urlText, isExtUrl, reqState) => urlText;
|
|
207
207
|
|
|
208
|
+
/** @typedef {import('../BuildRouter').BotContext} BotContext */
|
|
209
|
+
|
|
208
210
|
function processButtons (
|
|
209
211
|
buttons,
|
|
210
212
|
state,
|
|
211
213
|
elem,
|
|
212
|
-
linksMap,
|
|
213
214
|
senderId,
|
|
214
|
-
|
|
215
|
-
allowForbiddenSnippetWords,
|
|
215
|
+
context,
|
|
216
216
|
req,
|
|
217
217
|
res
|
|
218
218
|
) {
|
|
219
|
-
const translateLinks = linksTranslator || DEFAULT_LINK_TRANSLATOR;
|
|
219
|
+
const translateLinks = context.linksTranslator || DEFAULT_LINK_TRANSLATOR;
|
|
220
220
|
|
|
221
221
|
buttons.forEach(({
|
|
222
222
|
title: btnTitle,
|
|
@@ -230,7 +230,7 @@ function processButtons (
|
|
|
230
230
|
if (hasCondition) {
|
|
231
231
|
const condition = getCondition({
|
|
232
232
|
hasCondition, conditionFn, hasEditableCondition, editableCondition
|
|
233
|
-
}, 'Quick reply condition'
|
|
233
|
+
}, context, 'Quick reply condition');
|
|
234
234
|
|
|
235
235
|
if (!condition(req, res)) {
|
|
236
236
|
return;
|
|
@@ -259,7 +259,7 @@ function processButtons (
|
|
|
259
259
|
break;
|
|
260
260
|
}
|
|
261
261
|
case TYPE_POSTBACK: {
|
|
262
|
-
let postbackAction = linksMap.get(targetRouteId) || action;
|
|
262
|
+
let postbackAction = context.linksMap.get(targetRouteId) || action;
|
|
263
263
|
|
|
264
264
|
if (postbackAction === '/') {
|
|
265
265
|
postbackAction = './';
|
|
@@ -148,15 +148,16 @@ const compare = (variable, operator, value = undefined) => {
|
|
|
148
148
|
/**
|
|
149
149
|
*
|
|
150
150
|
* @param {{value:string, operator:string, variable:string}[][]} condition
|
|
151
|
+
* @param {object} configuration
|
|
151
152
|
* @param {string} description
|
|
152
153
|
*/
|
|
153
|
-
function customCondition (condition, description = '') {
|
|
154
|
+
function customCondition (condition, configuration, description = '') {
|
|
154
155
|
if (typeof condition !== 'object' || !Array.isArray(condition)) {
|
|
155
156
|
throw new Error(`Invalid condition (${description}) type`);
|
|
156
157
|
}
|
|
157
158
|
|
|
158
159
|
const resolver = (req, res) => condition.some((condList) => condList.every((cond) => {
|
|
159
|
-
const data = stateData(req, res);
|
|
160
|
+
const data = stateData(req, res, configuration);
|
|
160
161
|
const variableValue = getValue(cond.variable, data);
|
|
161
162
|
const isRegExp = [
|
|
162
163
|
ConditionOperators['matches regexp'],
|
|
@@ -6,7 +6,20 @@
|
|
|
6
6
|
const customCondition = require('./customCondition');
|
|
7
7
|
const customFn = require('./customFn');
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
/** @typedef {import('../BuildRouter').BotContext} BotContext */
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
*
|
|
13
|
+
* @param {object} params
|
|
14
|
+
* @param {BotContext} context
|
|
15
|
+
* @param {string} description
|
|
16
|
+
* @returns {Function}
|
|
17
|
+
*/
|
|
18
|
+
module.exports = function getCondition (params, context, description = '') {
|
|
19
|
+
const {
|
|
20
|
+
allowForbiddenSnippetWords = false,
|
|
21
|
+
configuration
|
|
22
|
+
} = context;
|
|
10
23
|
const {
|
|
11
24
|
hasCondition = false,
|
|
12
25
|
conditionFn = '() => true',
|
|
@@ -18,7 +31,7 @@ module.exports = (params, description = '', allowForbiddenSnippetWords = false)
|
|
|
18
31
|
|
|
19
32
|
if (hasCondition) {
|
|
20
33
|
if (hasEditableCondition) {
|
|
21
|
-
condition = customCondition(editableCondition);
|
|
34
|
+
condition = customCondition(editableCondition, configuration, description);
|
|
22
35
|
} else {
|
|
23
36
|
condition = customFn(conditionFn, description, allowForbiddenSnippetWords);
|
|
24
37
|
}
|
package/src/utils/getUpdate.js
CHANGED
|
@@ -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 {
|
|
@@ -88,7 +88,7 @@ function toArray (previousValue) {
|
|
|
88
88
|
const ENTITY_HBS_REGEXP = /^\s*\{\{\[?@([^@[\]{}\s]+)(\])?\}\}\s*$/;
|
|
89
89
|
const VARIABLE_HBS_REGEXP = /^\s*\{\{\[?([^@[\]{}\s]+)\]?\}\}\s*$/;
|
|
90
90
|
|
|
91
|
-
function getSetState (setState, req, res = null, useState = null) {
|
|
91
|
+
function getSetState (setState, req, res = null, useState = null, configuration = null) {
|
|
92
92
|
if (!setState) {
|
|
93
93
|
return {};
|
|
94
94
|
}
|
|
@@ -179,7 +179,7 @@ function getSetState (setState, req, res = null, useState = null) {
|
|
|
179
179
|
values = [];
|
|
180
180
|
} else {
|
|
181
181
|
const useValue = typeof value === 'string'
|
|
182
|
-
? handlebars.compile(value)(stateData(req, res))
|
|
182
|
+
? handlebars.compile(value)(stateData(req, res, configuration))
|
|
183
183
|
.split(/(?<!\\),/g)
|
|
184
184
|
.map((v) => v.replace(/\\,/g, ',').trim())
|
|
185
185
|
: value;
|
|
@@ -205,7 +205,7 @@ function getSetState (setState, req, res = null, useState = null) {
|
|
|
205
205
|
set = val;
|
|
206
206
|
}
|
|
207
207
|
} else if (typeof val === 'string') {
|
|
208
|
-
set = handlebars.compile(val)(stateData(req, res));
|
|
208
|
+
set = handlebars.compile(val)(stateData(req, res, configuration));
|
|
209
209
|
} else if (val === null
|
|
210
210
|
|| SCALAR_TYPES.includes(typeof val)) {
|
|
211
211
|
set = val;
|
package/src/utils/stateData.js
CHANGED
|
@@ -3,11 +3,14 @@
|
|
|
3
3
|
*/
|
|
4
4
|
'use strict';
|
|
5
5
|
|
|
6
|
-
module.exports = function stateData (req, res = null) {
|
|
6
|
+
module.exports = function stateData (req, res = null, configuration = null) {
|
|
7
|
+
const c = configuration || req.configuration;
|
|
7
8
|
return {
|
|
8
9
|
...req.state,
|
|
9
10
|
...(res ? res.newState : {}),
|
|
10
11
|
...req.actionData(),
|
|
11
|
-
...(res ? res.data : {})
|
|
12
|
+
...(res ? res.data : {}),
|
|
13
|
+
c,
|
|
14
|
+
configuration: c
|
|
12
15
|
};
|
|
13
16
|
};
|