wingbot 3.33.0 → 3.34.0-alpha.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wingbot",
3
- "version": "3.33.0",
3
+ "version": "3.34.0-alpha.1",
4
4
  "description": "Enterprise Messaging Bot Conversation Engine",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -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
- found = true;
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/Tester.js CHANGED
@@ -452,7 +452,7 @@ class Tester {
452
452
  }
453
453
 
454
454
  /**
455
- * Send quick reply if text exactly matches, otherwise returns false
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,38 @@ class Tester {
460
460
  * @memberOf Tester
461
461
  */
462
462
  async quickReplyText (text) {
463
+ let but = 'has not been found.';
463
464
 
464
465
  if (this.responses.length !== 0) {
465
- const search = tokenize(text);
466
+ const normalize = (t) => `${t}`.toLocaleLowerCase().replace(/\s+/g, ' ').trim();
467
+ const normalizedText = normalize(text);
468
+ const search = tokenize(normalizedText);
466
469
  const last = this.responses[this.responses.length - 1];
467
470
  const quickReplys = asserts.getQuickReplies(last);
468
- const res = quickReplys
471
+ let res = quickReplys
469
472
  .filter(({ title = '', payload }) => title && payload && tokenize(title) === search);
470
473
 
471
- if (res[0]) {
474
+ if (res.length > 1) {
475
+ res = res
476
+ .filter(({ title = '' }) => normalize(title) === normalizedText);
477
+ }
478
+
479
+ if (res.length === 1) {
472
480
  const { title, payload } = res[0];
473
481
  await this.processMessage(Request.quickReplyText(this.senderId, title, payload));
474
482
  return true;
475
483
  }
484
+
485
+ if (res.length > 1) {
486
+ but = 'found, but there are multiple occurences.';
487
+ }
488
+
489
+ but += quickReplys.length
490
+ ? ` (found: ${quickReplys.map((q) => q.title).filter((q) => !!q).join(', ')})`
491
+ : ' (no quick replies available)';
476
492
  }
477
493
 
478
- return false;
494
+ throw new Error(`Quick reply "${text}" ${but}`);
479
495
  }
480
496
 
481
497
  /**
@@ -14,6 +14,7 @@ const TYPE_SHARE = 'element_share';
14
14
  const TYPE_URL = 'web_url';
15
15
  const TYPE_URL_WITH_EXT = 'web_url_extension';
16
16
  const TYPE_POSTBACK = 'postback';
17
+ const TYPE_ATTACHMENT = 'attachment';
17
18
 
18
19
  const WEBVIEW_FULL = 'full';
19
20
  const WEBVIEW_TALL = 'tall';
@@ -227,10 +228,11 @@ function processButtons (
227
228
  editableCondition,
228
229
  setState
229
230
  }) => {
231
+
230
232
  if (hasCondition) {
231
233
  const condition = getCondition({
232
234
  hasCondition, conditionFn, hasEditableCondition, editableCondition
233
- }, context, 'Quick reply condition');
235
+ }, context, 'Button condition');
234
236
 
235
237
  if (!condition(req, res)) {
236
238
  return;
@@ -244,7 +246,8 @@ function processButtons (
244
246
  url,
245
247
  webviewHeight = WEBVIEW_TALL,
246
248
  targetRouteId,
247
- action
249
+ action,
250
+ payload
248
251
  } = btnAction;
249
252
 
250
253
  const isExtUrl = type === TYPE_URL_WITH_EXT;
@@ -273,6 +276,9 @@ function processButtons (
273
276
  case TYPE_SHARE:
274
277
  elem.shareButton(btnTitleText);
275
278
  break;
279
+ case TYPE_ATTACHMENT:
280
+ elem.attachmentButton(btnTitleText, payload);
281
+ break;
276
282
  default:
277
283
  }
278
284
  });
@@ -6,6 +6,46 @@
6
6
  const BaseTemplate = require('./BaseTemplate');
7
7
  const { makeAbsolute } = require('../utils');
8
8
 
9
+ /**
10
+ * @typedef MarkdownPayload
11
+ * @prop {"text/markdown"} contentType
12
+ * @prop {string} content
13
+ */
14
+
15
+ /**
16
+ * Designer payload type
17
+ *
18
+ * @typedef {MarkdownPayload} Payload
19
+ */
20
+
21
+ /**
22
+ * @typedef EncodedPayload
23
+ * @prop {string} content
24
+ * @prop {"text/markdown"} content_type
25
+ */
26
+
27
+ /**
28
+ * Encodes different types of payloads from designer snapshot to payload for chat
29
+ * Content is Base64 encoded.
30
+ *
31
+ * @param {Payload} payload
32
+ * @returns {EncodedPayload}
33
+ */
34
+ function encodePayload (payload) {
35
+ const encodedPayload = {
36
+ content: payload.content,
37
+ content_type: payload.contentType
38
+ };
39
+
40
+ switch (payload.contentType) {
41
+ default:
42
+ encodedPayload.content = Buffer.from(payload.content).toString('base64');
43
+ break;
44
+ }
45
+
46
+ return encodedPayload;
47
+ }
48
+
9
49
  /**
10
50
  * Helps with creating of button template
11
51
  * Instance of button template is returned by {Responder}
@@ -92,6 +132,31 @@ class ButtonTemplate extends BaseTemplate {
92
132
  return this;
93
133
  }
94
134
 
135
+ /**
136
+ * Adds button, which opens a popup with content on click.
137
+ *
138
+ * @param {string} title
139
+ * @param {Payload} payload
140
+ * @returns {this}
141
+ *
142
+ * @example
143
+ * res.button('text')
144
+ * .attachmentButton('button title', {
145
+ * content: '# Heading 1',
146
+ * contentType: 'text/markdown'
147
+ * })
148
+ * .send();
149
+ *
150
+ */
151
+ attachmentButton (title, payload) {
152
+ this.buttons.push({
153
+ type: 'attachment',
154
+ title: this._t(title),
155
+ payload: encodePayload(payload)
156
+ });
157
+ return this;
158
+ }
159
+
95
160
  /**
96
161
  *
97
162
  * @returns {this}
@@ -468,6 +468,7 @@ class CustomEntityDetectionModel {
468
468
  return {
469
469
  text: cleanText,
470
470
  intents: [],
471
+ // @ts-ignore
471
472
  entities
472
473
  };
473
474
  }
@@ -615,6 +616,28 @@ class CustomEntityDetectionModel {
615
616
  return this;
616
617
  }
617
618
 
619
+ /**
620
+ * Sets options to entity detector.
621
+ * Useful for disabling anonymization of local system entities.
622
+ *
623
+ * @param {string} name
624
+ * @param {object} options
625
+ * @param {boolean} [options.anonymize]
626
+ * @returns {this}
627
+ * @example
628
+ *
629
+ * ai.register('wingbot-model-name')
630
+ * .setDetectorOptions('phone', { anonymize: false })
631
+ * .setDetectorOptions('email', { anonymize: false })
632
+ */
633
+ setDetectorOptions (name, options) {
634
+ if (!this._entityDetectors.has(name)) {
635
+ throw new Error('Can\'t set entity detector options. Entity "name" does not exist.');
636
+ }
637
+ Object.assign(this._entityDetectors.get(name), options);
638
+ return this;
639
+ }
640
+
618
641
  async getPhrases () {
619
642
  return this._getPhrases();
620
643
  }