wingbot 3.76.16 → 3.76.17

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.76.16",
3
+ "version": "3.76.17",
4
4
  "description": "Enterprise Messaging Bot Conversation Engine",
5
5
  "main": "index.js",
6
6
  "type": "commonjs",
package/src/BotApp.js CHANGED
@@ -418,7 +418,7 @@ class BotApp {
418
418
 
419
419
  const sender = new BotAppSender(options, senderId, message, this._senderLogger);
420
420
  const res = await this._processor
421
- .processMessage(message, pageId, sender, { ...data, appId });
421
+ .processMessage(message, pageId, sender, { passPostbacks: true, ...data, appId });
422
422
 
423
423
  return res;
424
424
  }
@@ -48,6 +48,7 @@ const BASE64_REGEX = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{
48
48
  /**
49
49
  * @typedef {object} DispatcherOptions
50
50
  * @prop {Logger} [log]
51
+ * @prop {boolean} [allowRefs]
51
52
  */
52
53
 
53
54
  /**
@@ -76,7 +77,8 @@ class LLMDispatcher {
76
77
  actionList: [],
77
78
  router: null,
78
79
  byAction: new Map(),
79
- routes: []
80
+ routes: [],
81
+ structuredOutput: null
80
82
  },
81
83
  locals: new Map()
82
84
  };
@@ -98,6 +100,16 @@ class LLMDispatcher {
98
100
  this._dispatched = false;
99
101
 
100
102
  this._overrideAction = null;
103
+
104
+ this._allowRefs = !!options.allowRefs;
105
+ }
106
+
107
+ /**
108
+ *
109
+ * @param {boolean} [allow]
110
+ */
111
+ allowRefs (allow = true) {
112
+ this._allowRefs = allow;
101
113
  }
102
114
 
103
115
  /**
@@ -137,7 +149,8 @@ class LLMDispatcher {
137
149
  */
138
150
  async _postbackAction (responder = null) {
139
151
  let res;
140
- if (this._req.referral !== null && this._req.referral.ref) {
152
+ // @todo vypnout
153
+ if (this._allowRefs && this._req.referral !== null && this._req.referral.ref) {
141
154
  res = parseActionPayload({ payload: this._req.referral.ref });
142
155
  }
143
156
 
@@ -145,16 +158,24 @@ class LLMDispatcher {
145
158
  res = parseActionPayload(this._req.postback);
146
159
  }
147
160
 
148
- if (!res && this._req.optin !== null && this._req.optin.ref) {
161
+ if (this._allowRefs && !res && this._req.optin !== null && this._req.optin.ref) {
149
162
  res = this._base64Ref(this._req.optin);
150
163
  }
151
164
 
152
- if (!res && this._req.optin !== null && this._req.optin.payload) {
165
+ if (this._allowRefs && !res && this._req.optin !== null && this._req.optin.payload) {
153
166
  res = parseActionPayload(this._req.optin);
154
167
  }
155
168
 
156
169
  if (!res && this._req.message !== null && this._req.message.quick_reply) {
157
- res = parseActionPayload(this._req.message.quick_reply);
170
+ const { payload, ...rest } = this._req.message.quick_reply;
171
+ const { _qrsPaylMap: payloadMap = {} } = this._req.state;
172
+
173
+ if (payload && payloadMap[payload]) {
174
+ res = parseActionPayload({
175
+ ...rest,
176
+ payload: payloadMap[payload]
177
+ });
178
+ }
158
179
  }
159
180
 
160
181
  /**
package/src/Processor.js CHANGED
@@ -117,6 +117,7 @@ const LLMDispatcher = require('./LLMDispatcher');
117
117
  * @prop {LLMGlobalConfig} [llm] - LLM model configuration
118
118
  * @prop {Preloader<R>} [preloader]
119
119
  * @prop {string[]} [allowedPostBacks]
120
+ * @prop {boolean} [allowRefs]
120
121
  */
121
122
 
122
123
  /**
@@ -210,7 +211,8 @@ class Processor extends EventEmitter {
210
211
  redirectLimit: 20,
211
212
  nameFromState: NAME_FROM_STATE,
212
213
  sessionDuration: 1800000, // 30 minutes,
213
- allowedPostBacks: []
214
+ allowedPostBacks: [],
215
+ allowRefs: false
214
216
  };
215
217
 
216
218
  Object.assign(this.options, options);
@@ -535,8 +537,9 @@ class Processor extends EventEmitter {
535
537
  ? Request.text('none', text)
536
538
  : text;
537
539
  // @ts-ignore
538
- const dispatcher = new LLMDispatcher(Ai.ai, this.reducer.routing, {
539
- log: this.options.log
540
+ const dispatcher = new LLMDispatcher(Ai.ai, this.reducer.routing, null, {
541
+ log: this.options.log,
542
+ allowRefs: this.options.allowRefs
540
543
  });
541
544
  const req = new Request(request, { lang }, pageId, dispatcher);
542
545
  await dispatcher.dispatch(req);
@@ -643,7 +646,8 @@ class Processor extends EventEmitter {
643
646
  }
644
647
 
645
648
  const dispatcher = new LLMDispatcher(Ai.ai, this.reducer.routing, llm, {
646
- log: this.options.log
649
+ log: this.options.log,
650
+ allowRefs: this.options.allowRefs
647
651
  });
648
652
  req = new Request(
649
653
  message,
@@ -663,7 +667,9 @@ class Processor extends EventEmitter {
663
667
  if (req.isPostBack()
664
668
  && fromEvent
665
669
  && reqActionNorm
670
+ && !responderData.passPostbacks
666
671
  && this.options.allowedPostBacks.length > 0
672
+ && !req.postBackHasObscuredPayload()
667
673
  && !this.options.allowedPostBacks.some((pb) => {
668
674
  const norm = pb.replace(/^\/?/, '/');
669
675
  return norm === reqActionNorm;
package/src/Request.js CHANGED
@@ -175,7 +175,20 @@ class Request {
175
175
 
176
176
  this.message = event.message || null;
177
177
 
178
- this.postback = event.postback || null;
178
+ let usePostBack;
179
+ if (!event.postback) {
180
+ usePostBack = null;
181
+ } else if (this.postBackHasObscuredPayload()) {
182
+ const { payload, ...rest } = event.postback;
183
+ usePostBack = {
184
+ payload: state[`_PB${event.postback.payload}`],
185
+ ...rest
186
+ };
187
+ } else {
188
+ usePostBack = event.postback;
189
+ }
190
+
191
+ this.postback = usePostBack;
179
192
 
180
193
  this.referral = (this.postback && this.postback.referral)
181
194
  || event.referral
@@ -292,6 +305,11 @@ class Request {
292
305
  this.configuration = configuration;
293
306
  }
294
307
 
308
+ postBackHasObscuredPayload () {
309
+ return typeof this._event.postback?.payload === 'string'
310
+ && this.state?.[`_PB${this._event.postback.payload}`];
311
+ }
312
+
295
313
  /**
296
314
  * @returns {LLMDispatcher}
297
315
  */
@@ -1532,10 +1550,12 @@ It looks like the bot isn't connected to class BotApp or the Processor is used w
1532
1550
  message: {
1533
1551
  text: action,
1534
1552
  quick_reply: {
1535
- payload: JSON.stringify({
1536
- action,
1537
- data
1538
- })
1553
+ payload: data === null
1554
+ ? action
1555
+ : JSON.stringify({
1556
+ action,
1557
+ data
1558
+ })
1539
1559
  }
1540
1560
  }
1541
1561
  };
package/src/Responder.js CHANGED
@@ -3,6 +3,7 @@
3
3
  */
4
4
  'use strict';
5
5
 
6
+ const crypto = require('crypto');
6
7
  const Ai = require('./Ai');
7
8
  const ReceiptTemplate = require('./templates/ReceiptTemplate');
8
9
  const ButtonTemplate = require('./templates/ButtonTemplate');
@@ -944,17 +945,36 @@ class Responder {
944
945
  };
945
946
  }
946
947
 
947
- if (qrs.length > 0) {
948
- this.finalMessageSent = true;
949
- Object.assign(messageData.message, {
950
- quick_replies: qrs
951
- });
952
-
953
- this._addExpectedIntents(expectedKeywords);
954
- this._quickReplyCollector = [];
948
+ if (qrs.length === 0) {
949
+ return qrs;
955
950
  }
956
951
 
957
- return qrs;
952
+ const qrsPaylMap = {};
953
+
954
+ const mappedQrs = qrs.map((qr, i) => {
955
+ if (!qr.payload) {
956
+ return qr;
957
+ }
958
+ const payload = crypto.randomBytes(16).toString('hex') + (i % 36).toString(36);
959
+ qrsPaylMap[payload] = qr.payload;
960
+ return {
961
+ ...qr,
962
+ payload
963
+ };
964
+ });
965
+
966
+ this.setState({
967
+ _qrsPaylMap: qrsPaylMap
968
+ });
969
+
970
+ this.finalMessageSent = true;
971
+ Object.assign(messageData.message, {
972
+ quick_replies: mappedQrs
973
+ });
974
+
975
+ this._addExpectedIntents(expectedKeywords);
976
+ this._quickReplyCollector = [];
977
+ return mappedQrs;
958
978
  }
959
979
 
960
980
  /**
@@ -1697,7 +1717,8 @@ class Responder {
1697
1717
  */
1698
1718
  button (text) {
1699
1719
  const btn = new ButtonTemplate(
1700
- (payload) => {
1720
+ (payload, setState) => {
1721
+ this.setState(setState);
1701
1722
  this._textResponses.push(text);
1702
1723
  this.template(payload);
1703
1724
  },
@@ -1729,7 +1750,10 @@ class Responder {
1729
1750
  */
1730
1751
  genericTemplate (shareable = false, isSquare = false) {
1731
1752
  return new GenericTemplate(
1732
- (payload) => this.template(payload),
1753
+ (payload, setState) => {
1754
+ this.setState(setState);
1755
+ this.template(payload);
1756
+ },
1733
1757
  this._createContext(),
1734
1758
  shareable,
1735
1759
  isSquare
package/src/Tester.js CHANGED
@@ -110,6 +110,7 @@ class Tester {
110
110
  llm: {
111
111
  ...processorOptions.llm
112
112
  },
113
+ allowRefs: true,
113
114
  ...processorOptions
114
115
  }));
115
116
 
@@ -248,7 +249,9 @@ class Tester {
248
249
  messageSender.simulatesOptIn = true;
249
250
 
250
251
  const res = await this.processor
251
- .processMessage(message, pageId, messageSender, { ...data, ...this.testData });
252
+ .processMessage(message, pageId, messageSender, {
253
+ passPostbacks: false, ...data, ...this.testData
254
+ });
252
255
  this._acquireResponseActions(res, messageSender);
253
256
 
254
257
  return res;
@@ -290,7 +293,7 @@ class Tester {
290
293
  assert.fail(`Response ${index} does not exists. There are ${this.responses.length} responses`);
291
294
  }
292
295
 
293
- return new ResponseAssert(this.responses[index]);
296
+ return new ResponseAssert(this.responses[index], this.getState());
294
297
  }
295
298
 
296
299
  /**
@@ -301,7 +304,7 @@ class Tester {
301
304
  * @memberOf Tester
302
305
  */
303
306
  any () {
304
- return new AnyResponseAssert(this.responses);
307
+ return new AnyResponseAssert(this.responses, this.getState());
305
308
  }
306
309
 
307
310
  /**
@@ -315,7 +318,7 @@ class Tester {
315
318
  if (this.responses.length === 0) {
316
319
  assert.fail('Theres no response');
317
320
  }
318
- return new ResponseAssert(this.responses[this.responses.length - 1]);
321
+ return new ResponseAssert(this.responses[this.responses.length - 1], this.getState());
319
322
  }
320
323
 
321
324
  _actionMatches (botAction, path) {
@@ -612,7 +615,7 @@ class Tester {
612
615
  quickReply (action, data = {}) {
613
616
  if (this.responses.length !== 0) {
614
617
  const last = this.responses[this.responses.length - 1];
615
- const quickReplys = asserts.getQuickReplies(last);
618
+ const quickReplys = asserts.getQuickReplies(last, this.getState());
616
619
  const res = quickReplys
617
620
  .filter((reply) => {
618
621
  const { action: route } = parseActionPayload(reply, true);
@@ -620,8 +623,10 @@ class Tester {
620
623
  });
621
624
 
622
625
  if (res[0]) {
623
- const { title, payload } = res[0];
624
- return this.processMessage(Request.quickReplyText(this.senderId, title, payload));
626
+ const { title, payload, _origPayload: origPayload } = res[0];
627
+ return this.processMessage(
628
+ Request.quickReplyText(this.senderId, title, origPayload || payload)
629
+ );
625
630
  }
626
631
  }
627
632
 
@@ -38,6 +38,8 @@ class BaseTemplate {
38
38
  Object.assign(this.context, context);
39
39
 
40
40
  this._t = this.context.translator;
41
+
42
+ this._setState = {};
41
43
  }
42
44
 
43
45
  getTemplate () {
@@ -45,7 +47,7 @@ class BaseTemplate {
45
47
  }
46
48
 
47
49
  send () {
48
- this.onDone(this.getTemplate());
50
+ this.onDone(this.getTemplate(), this._setState);
49
51
  }
50
52
 
51
53
  _imageUrl (image) {
@@ -3,6 +3,7 @@
3
3
  */
4
4
  'use strict';
5
5
 
6
+ const crypto = require('crypto');
6
7
  const BaseTemplate = require('./BaseTemplate');
7
8
  const { makeAbsolute } = require('../utils');
8
9
 
@@ -138,7 +139,7 @@ class ButtonTemplate extends BaseTemplate {
138
139
  _createPayload (action, data, setState = null) {
139
140
  const hasSetState = setState && Object.keys(setState).length !== 0;
140
141
 
141
- return JSON.stringify({
142
+ const actionData = JSON.stringify({
142
143
  action: makeAbsolute(action, this.context.path),
143
144
  data: {
144
145
  _ca: this.context.currentAction,
@@ -146,6 +147,17 @@ class ButtonTemplate extends BaseTemplate {
146
147
  },
147
148
  ...(hasSetState ? { setState } : {})
148
149
  });
150
+
151
+ const payload = crypto
152
+ .createHash('shake256', { outputLength: 16 })
153
+ .update(actionData)
154
+ .digest('hex');
155
+
156
+ Object.assign(this._setState, {
157
+ [`_PB${payload}`]: actionData
158
+ });
159
+
160
+ return payload;
149
161
  }
150
162
 
151
163
  /**
@@ -4,7 +4,6 @@
4
4
  'use strict';
5
5
 
6
6
  const ButtonTemplate = require('./ButtonTemplate');
7
- const { makeAbsolute } = require('../utils');
8
7
 
9
8
  /**
10
9
  * Generic template utility
@@ -84,19 +83,10 @@ class GenericTemplate extends ButtonTemplate {
84
83
  * @memberOf GenericTemplate
85
84
  */
86
85
  setElementActionPostback (action, data = {}, setState = null) {
87
- const hasSetState = setState && Object.keys(setState).length !== 0;
88
-
89
86
  Object.assign(this._element, {
90
87
  default_action: {
91
88
  type: 'postback',
92
- payload: JSON.stringify({
93
- action: makeAbsolute(action, this.context.path),
94
- data: {
95
- _ca: this.context.currentAction,
96
- ...data
97
- },
98
- ...(hasSetState ? { setState } : {})
99
- })
89
+ payload: this._createPayload(action, data, setState)
100
90
  }
101
91
  });
102
92
  return this;
@@ -15,8 +15,9 @@ const m = asserts.ex;
15
15
  */
16
16
  class AnyResponseAssert {
17
17
 
18
- constructor (responses = []) {
18
+ constructor (responses = [], state = {}) {
19
19
  this.responses = responses;
20
+ this.state = state;
20
21
  }
21
22
 
22
23
  /**
@@ -69,10 +70,10 @@ class AnyResponseAssert {
69
70
  */
70
71
  quickReplyAction (action) {
71
72
  const ok = this.responses
72
- .some((res) => asserts.quickReplyAction(res, action, false));
73
+ .some((res) => asserts.quickReplyAction(res, action, false, this.state));
73
74
  if (!ok) {
74
75
  const actual = this.responses
75
- .map((res) => asserts.getQuickReplies(res))
76
+ .map((res) => asserts.getQuickReplies(res, this.state))
76
77
  .filter((replies) => !!replies)
77
78
  .reduce((a, replies) => {
78
79
  for (const reply of replies) {
@@ -98,10 +99,10 @@ class AnyResponseAssert {
98
99
  */
99
100
  quickReplyTextContains (search) {
100
101
  const ok = this.responses
101
- .some((res) => asserts.quickReplyText(res, search, false));
102
+ .some((res) => asserts.quickReplyText(res, search, false, this.state));
102
103
  if (!ok) {
103
104
  const actual = this.responses
104
- .map((res) => asserts.getQuickReplies(res))
105
+ .map((res) => asserts.getQuickReplies(res, this.state))
105
106
  .filter((replies) => !!replies)
106
107
  .reduce((a, replies) => {
107
108
  for (const { title } of replies) {
@@ -12,8 +12,9 @@ const asserts = require('./asserts');
12
12
  */
13
13
  class ResponseAssert {
14
14
 
15
- constructor (response = {}) {
15
+ constructor (response = {}, state = {}) {
16
16
  this.response = response;
17
+ this.state = state;
17
18
  }
18
19
 
19
20
  waiting () {
@@ -43,7 +44,7 @@ class ResponseAssert {
43
44
  * @memberOf ResponseAssert
44
45
  */
45
46
  quickReplyAction (action) {
46
- asserts.quickReplyAction(this.response, action);
47
+ asserts.quickReplyAction(this.response, action, undefined, this.state);
47
48
  return this;
48
49
  }
49
50
 
@@ -65,8 +65,20 @@ function getText (response) {
65
65
  return null;
66
66
  }
67
67
 
68
- function getQuickReplies (response) {
69
- return (response.message && response.message.quick_replies) || [];
68
+ function getQuickReplies (response, state = {}) {
69
+ const { state: { _qrsPaylMap: qrMap = {} } = {} } = state;
70
+ const qrs = (response.message && response.message.quick_replies) || [];
71
+ return qrs.map((qr) => {
72
+ // eslint-disable-next-line prefer-const
73
+ let { payload, ...rst } = qr;
74
+ if (!payload) return rst;
75
+ if (qrMap[payload]) payload = qrMap[payload];
76
+ return {
77
+ _origPayload: qr.payload,
78
+ ...rst,
79
+ payload
80
+ };
81
+ });
70
82
  }
71
83
 
72
84
  function getAttachment (response) {
@@ -187,10 +199,11 @@ function llmContains (llmMsg, search, message = 'Should contain a text') {
187
199
  * @param {object} response
188
200
  * @param {string} action
189
201
  * @param {string|false} [message='Should contain the action'] - use false for no asserts
202
+ * @param {{}} [state={}] - bot state to replace qrs
190
203
  * @returns {boolean}
191
204
  */
192
- function quickReplyAction (response, action, message = 'Should contain the action') {
193
- const replies = getQuickReplies(response);
205
+ function quickReplyAction (response, action, message = 'Should contain the action', state = {}) {
206
+ const replies = getQuickReplies(response, state);
194
207
  const hasItems = replies.length > 0;
195
208
  if (message === false && !hasItems) {
196
209
  return false;