wingbot 3.30.0 → 3.31.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.
|
|
3
|
+
"version": "3.31.0",
|
|
4
4
|
"description": "Enterprise Messaging Bot Conversation Engine",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -53,6 +53,7 @@
|
|
|
53
53
|
"tsd-jsdoc": "^2.5.0"
|
|
54
54
|
},
|
|
55
55
|
"dependencies": {
|
|
56
|
+
"form-data": "^4.0.0",
|
|
56
57
|
"graphql": "^15.8.0",
|
|
57
58
|
"jsonwebtoken": "^8.5.1",
|
|
58
59
|
"node-fetch": "^2.6.7",
|
package/src/BotAppSender.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
const jwt = require('jsonwebtoken');
|
|
7
7
|
const { default: fetch, Headers } = require('node-fetch');
|
|
8
|
+
const FormData = require('form-data');
|
|
8
9
|
const crypto = require('crypto');
|
|
9
10
|
const { Agent } = require('https');
|
|
10
11
|
const { promisify } = require('util');
|
|
@@ -12,6 +13,7 @@ const ReturnSender = require('./ReturnSender');
|
|
|
12
13
|
|
|
13
14
|
/** @typedef {import('./ReturnSender').ReturnSenderOptions} ReturnSenderOptions */
|
|
14
15
|
/** @typedef {import('./ReturnSender').ChatLogStorage} ChatLogStorage */
|
|
16
|
+
/** @typedef {import('./ReturnSender').UploadResult} UploadResult */
|
|
15
17
|
|
|
16
18
|
/**
|
|
17
19
|
* @typedef {object} TlsOptions
|
|
@@ -91,6 +93,38 @@ class BotAppSender extends ReturnSender {
|
|
|
91
93
|
return this._agent;
|
|
92
94
|
}
|
|
93
95
|
|
|
96
|
+
/**
|
|
97
|
+
*
|
|
98
|
+
* @param {Buffer} data
|
|
99
|
+
* @param {string} contentType
|
|
100
|
+
* @param {string} fileName
|
|
101
|
+
* @returns {Promise<UploadResult>}
|
|
102
|
+
*/
|
|
103
|
+
async upload (data, contentType, fileName) {
|
|
104
|
+
const formData = new FormData();
|
|
105
|
+
|
|
106
|
+
const nonce = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER).toString(36).padEnd(11, '0');
|
|
107
|
+
|
|
108
|
+
formData.append('nonce', nonce);
|
|
109
|
+
formData.append('f0', data, { filename: fileName, contentType });
|
|
110
|
+
|
|
111
|
+
const [token, agent] = await Promise.all([
|
|
112
|
+
BotAppSender.signBody(nonce, this._secret, this._appId),
|
|
113
|
+
this._getAgent()
|
|
114
|
+
]);
|
|
115
|
+
|
|
116
|
+
const headers = new Headers();
|
|
117
|
+
|
|
118
|
+
headers.set('Authorization', token);
|
|
119
|
+
|
|
120
|
+
const response = await this._fetch(this._apiUrl, {
|
|
121
|
+
headers, body: formData, agent, method: 'POST'
|
|
122
|
+
})
|
|
123
|
+
.then((r) => r.json());
|
|
124
|
+
|
|
125
|
+
return response;
|
|
126
|
+
}
|
|
127
|
+
|
|
94
128
|
async _send (payload) {
|
|
95
129
|
// attach sender
|
|
96
130
|
if (typeof payload.sender === 'undefined') {
|
package/src/Responder.js
CHANGED
|
@@ -60,6 +60,8 @@ Object.freeze(ExpectedInput);
|
|
|
60
60
|
* @prop {string} [voice]
|
|
61
61
|
*/
|
|
62
62
|
|
|
63
|
+
/** @typedef {import('./ReturnSender').UploadResult} UploadResult */
|
|
64
|
+
|
|
63
65
|
/**
|
|
64
66
|
* Instance of responder is passed as second parameter of handler (res)
|
|
65
67
|
*
|
|
@@ -537,6 +539,7 @@ class Responder {
|
|
|
537
539
|
});
|
|
538
540
|
} else {
|
|
539
541
|
this._quickReplyCollector.push({
|
|
542
|
+
// @ts-ignore
|
|
540
543
|
action: this.toAbsoluteAction(action),
|
|
541
544
|
title,
|
|
542
545
|
data,
|
|
@@ -749,6 +752,30 @@ class Responder {
|
|
|
749
752
|
return ret;
|
|
750
753
|
}
|
|
751
754
|
|
|
755
|
+
/**
|
|
756
|
+
*
|
|
757
|
+
* @param {Buffer} data
|
|
758
|
+
* @param {string} contentType
|
|
759
|
+
* @param {string} fileName
|
|
760
|
+
* @returns {Promise<UploadResult>}
|
|
761
|
+
*/
|
|
762
|
+
async upload (data, contentType, fileName) {
|
|
763
|
+
const result = await this._messageSender.upload(data, contentType, fileName);
|
|
764
|
+
|
|
765
|
+
if (!result.url) {
|
|
766
|
+
throw new Error(`Got no url on file upload ${fileName}. Probably a compatibility issue.`);
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
let [type] = contentType.split('/');
|
|
770
|
+
|
|
771
|
+
if (!['image', 'video', 'audio'].includes(type)) {
|
|
772
|
+
type = 'file';
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
this._attachment(result.url, type, true);
|
|
776
|
+
return result;
|
|
777
|
+
}
|
|
778
|
+
|
|
752
779
|
/**
|
|
753
780
|
* Sends image as response. Requires appUrl option to send images from server
|
|
754
781
|
*
|
package/src/ReturnSender.js
CHANGED
|
@@ -23,6 +23,12 @@ const { FLAG_DO_NOT_LOG } = require('./flags');
|
|
|
23
23
|
* @prop {textFilter} [confidentInputFilter] - filter for confident input (@CONFIDENT)
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
|
+
/**
|
|
27
|
+
* @typedef {object} UploadResult
|
|
28
|
+
* @prop {string} [url]
|
|
29
|
+
* @prop {string|number} [attachmentId]
|
|
30
|
+
*/
|
|
31
|
+
|
|
26
32
|
/**
|
|
27
33
|
* Text filter function
|
|
28
34
|
*
|
|
@@ -319,6 +325,17 @@ class ReturnSender {
|
|
|
319
325
|
this._visitedInteractions.push(action);
|
|
320
326
|
}
|
|
321
327
|
|
|
328
|
+
/**
|
|
329
|
+
*
|
|
330
|
+
* @param {Buffer} data
|
|
331
|
+
* @param {string} contentType
|
|
332
|
+
* @param {string} fileName
|
|
333
|
+
* @returns {Promise<UploadResult>}
|
|
334
|
+
*/
|
|
335
|
+
async upload (data, contentType, fileName) { // eslint-disable-line no-unused-vars
|
|
336
|
+
throw new Error('#upload() not supported by this channel');
|
|
337
|
+
}
|
|
338
|
+
|
|
322
339
|
send (payload) {
|
|
323
340
|
if (this._finished) {
|
|
324
341
|
throw new Error('Cannot send message after sender is finished');
|
|
@@ -452,7 +452,7 @@ class NotificationsStorage {
|
|
|
452
452
|
* @param {string} tag
|
|
453
453
|
* @returns {Promise}
|
|
454
454
|
*/
|
|
455
|
-
|
|
455
|
+
_subscribe (senderId, pageId, tag) {
|
|
456
456
|
const key = `${senderId}|${pageId}`;
|
|
457
457
|
let subscribtion = this._subscribtions.get(key);
|
|
458
458
|
if (!subscribtion) {
|
|
@@ -467,6 +467,18 @@ class NotificationsStorage {
|
|
|
467
467
|
}
|
|
468
468
|
|
|
469
469
|
this._subscribtions.set(key, subscribtion);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
*
|
|
474
|
+
* @param {string|string[]} senderId
|
|
475
|
+
* @param {string} pageId
|
|
476
|
+
* @param {string} tag
|
|
477
|
+
* @returns {Promise}
|
|
478
|
+
*/
|
|
479
|
+
subscribe (senderId, pageId, tag) {
|
|
480
|
+
const insert = Array.isArray(senderId) ? senderId : [senderId];
|
|
481
|
+
insert.forEach((sender) => this._subscribe(sender, pageId, tag));
|
|
470
482
|
return Promise.resolve();
|
|
471
483
|
}
|
|
472
484
|
|
|
@@ -198,12 +198,16 @@ function notificationsApiFactory (storage, notifications, acl, options = {}) {
|
|
|
198
198
|
);
|
|
199
199
|
}
|
|
200
200
|
|
|
201
|
+
senderIds = senderIds.slice();
|
|
202
|
+
|
|
201
203
|
// make it little bit faster
|
|
202
204
|
while (senderIds.length > 0) {
|
|
203
|
-
|
|
204
|
-
senderIds.splice(0,
|
|
205
|
-
|
|
206
|
-
|
|
205
|
+
if (storage.subscribe.length > 3) {
|
|
206
|
+
const insertSenders = senderIds.splice(0, 500);
|
|
207
|
+
await storage.subscribe(insertSenders, pageId, tag, true);
|
|
208
|
+
} else {
|
|
209
|
+
await storage.subscribe(senderIds.pop(), pageId, tag);
|
|
210
|
+
}
|
|
207
211
|
}
|
|
208
212
|
|
|
209
213
|
return true;
|
package/src/resolvers/message.js
CHANGED
|
@@ -9,20 +9,27 @@ const Request = require('../Request');
|
|
|
9
9
|
const Router = require('../Router');
|
|
10
10
|
const getCondition = require('../utils/getCondition');
|
|
11
11
|
const {
|
|
12
|
-
stateData,
|
|
12
|
+
stateData,
|
|
13
|
+
getLanguageText,
|
|
14
|
+
getLanguageTextObjects,
|
|
15
|
+
randomizedCompiler,
|
|
16
|
+
cachedTranslatedCompilator
|
|
13
17
|
} = require('./utils');
|
|
14
18
|
const {
|
|
15
19
|
FEATURE_SSML, FEATURE_TEXT, FEATURE_VOICE
|
|
16
20
|
} = require('../features');
|
|
17
21
|
|
|
22
|
+
/** @typedef {import('../Responder').VoiceControl} VoiceControl */
|
|
23
|
+
/** @typedef {import('./utils').Translations} Translations */
|
|
24
|
+
/** @typedef {import('./utils').TextObject} TextObject */
|
|
18
25
|
/**
|
|
19
26
|
* Returns voice control props from params
|
|
20
27
|
*
|
|
21
28
|
* @param {any} params
|
|
22
29
|
* @param {string} lang
|
|
23
|
-
* @returns {null |
|
|
30
|
+
* @returns {null | VoiceControl}
|
|
24
31
|
*/
|
|
25
|
-
function
|
|
32
|
+
function getVoiceControlFromParams (params, lang = null) {
|
|
26
33
|
const voiceControl = {};
|
|
27
34
|
|
|
28
35
|
const voiceControlProps = ['speed', 'pitch', 'volume', 'voice', 'style', 'language'];
|
|
@@ -75,7 +82,7 @@ function parseReplies (replies, linksMap, allowForbiddenSnippetWords) {
|
|
|
75
82
|
action,
|
|
76
83
|
condition,
|
|
77
84
|
title: reply.title
|
|
78
|
-
?
|
|
85
|
+
? cachedTranslatedCompilator(reply.title)
|
|
79
86
|
: null,
|
|
80
87
|
data: {}
|
|
81
88
|
};
|
|
@@ -101,10 +108,10 @@ function parseReplies (replies, linksMap, allowForbiddenSnippetWords) {
|
|
|
101
108
|
}
|
|
102
109
|
|
|
103
110
|
/**
|
|
104
|
-
* @param {
|
|
111
|
+
* @param {Translations} text
|
|
105
112
|
* @param {string[]} features
|
|
106
113
|
* @param {string} lang
|
|
107
|
-
* @returns {{translations:
|
|
114
|
+
* @returns {{translations:TextObject[],ssmlAlternatives:string[] | null}}
|
|
108
115
|
*/
|
|
109
116
|
function findSupportedMessages (text, features, lang = null) {
|
|
110
117
|
let translations = getLanguageTextObjects(text, lang);
|
|
@@ -180,7 +187,6 @@ function message (params, {
|
|
|
180
187
|
const data = stateData(req, res);
|
|
181
188
|
|
|
182
189
|
// filter supported messages
|
|
183
|
-
/** @type {{translations:import('./utils').TextObject[],ssmlAlternatives:string[]}} */
|
|
184
190
|
const supportedText = findSupportedMessages(
|
|
185
191
|
params.text,
|
|
186
192
|
req.features,
|
|
@@ -231,7 +237,7 @@ function message (params, {
|
|
|
231
237
|
}
|
|
232
238
|
|
|
233
239
|
// generate voice control
|
|
234
|
-
let voiceControl =
|
|
240
|
+
let voiceControl = getVoiceControlFromParams(params, data.lang);
|
|
235
241
|
if (supportedText.ssmlAlternatives) {
|
|
236
242
|
// find SSML alternative
|
|
237
243
|
const ssmlAlternativeTemplate = randomizedCompiler([{
|