chat-platform 1.3.2 → 2.1.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/.eslintrc +23 -16
- package/__tests__/context-provider-memory.js +148 -189
- package/__tests__/context-provider-sqlite.js +49 -82
- package/__tests__/universal-platform.js +37 -86
- package/chat-platform.js +86 -102
- package/helpers/lcd.js +4 -0
- package/package.json +4 -3
- package/providers/memory.js +69 -29
- package/providers/plain-file.js +32 -3
- package/providers/sqlite.js +109 -39
- package/__tests__/context-provider-plain-file.js +0 -174
|
@@ -82,57 +82,48 @@ describe('Universal Connector', () => {
|
|
|
82
82
|
resolve(message);
|
|
83
83
|
})
|
|
84
84
|
);
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
it('send outgoing message', async () => {
|
|
89
|
+
await chatServer.start()
|
|
90
|
+
const message = await chatServer.createMessage('42', '1234', '4567', {
|
|
91
|
+
payload: {
|
|
92
|
+
type: 'a-message-type',
|
|
93
|
+
chatId: '42'
|
|
91
94
|
}
|
|
92
|
-
return null;
|
|
93
95
|
});
|
|
94
|
-
|
|
96
|
+
let sentMessage = await chatServer.send(message);
|
|
97
|
+
|
|
98
|
+
assert.equal(sentMessage.message_id, '444');
|
|
99
|
+
expect(sendFunction).to.have.been.called();
|
|
95
100
|
|
|
96
|
-
it('receive incoming message', () => {
|
|
97
|
-
chatServer.start()
|
|
98
|
-
.then(() => {
|
|
99
|
-
chatServer.on('message', message => {
|
|
100
|
-
assert.equal(message.payload.type, 'message');
|
|
101
|
-
assert.equal(message.custom_value, 42);
|
|
102
|
-
assert.equal(message.payload.chatId, '42');
|
|
103
|
-
assert.equal(message.originalMessage.messageId, 'xyz');
|
|
104
|
-
assert.equal(message.originalMessage.userId, 24);
|
|
105
|
-
assert.isFunction(message.chat);
|
|
106
|
-
const variables = message.chat().all();
|
|
107
|
-
assert.equal(variables.payload, 'Bazinga');
|
|
108
|
-
assert.equal(variables.transport, 'universal');
|
|
109
|
-
assert.equal(variables.authorized, false);
|
|
110
|
-
assert.equal(variables.language, 'it');
|
|
111
|
-
assert.instanceOf(message.payload.ts, moment);
|
|
112
|
-
});
|
|
113
|
-
chatServer.receive({
|
|
114
|
-
text: 'Bazinga',
|
|
115
|
-
lang: 'it',
|
|
116
|
-
chat_id: '42',
|
|
117
|
-
_id: 'xyz',
|
|
118
|
-
user: { id: 24 }
|
|
119
|
-
});
|
|
120
|
-
});
|
|
121
101
|
});
|
|
122
102
|
|
|
123
|
-
it('
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
.
|
|
132
|
-
.
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
103
|
+
it('receive incoming message', async (done) => {
|
|
104
|
+
await chatServer.start()
|
|
105
|
+
|
|
106
|
+
chatServer.on('message', message => {
|
|
107
|
+
assert.equal(message.payload.type, 'message');
|
|
108
|
+
assert.equal(message.custom_value, 42);
|
|
109
|
+
assert.equal(message.payload.chatId, '42');
|
|
110
|
+
assert.equal(message.originalMessage.messageId, 'xyz');
|
|
111
|
+
assert.equal(message.originalMessage.userId, 42);
|
|
112
|
+
assert.isFunction(message.chat);
|
|
113
|
+
const variables = message.chat().all();
|
|
114
|
+
assert.equal(variables.payload, 'Bazinga');
|
|
115
|
+
assert.equal(variables.authorized, false);
|
|
116
|
+
assert.equal(variables.language, 'it');
|
|
117
|
+
assert.instanceOf(message.payload.ts, moment);
|
|
118
|
+
done();
|
|
119
|
+
});
|
|
120
|
+
chatServer.receive({
|
|
121
|
+
text: 'Bazinga',
|
|
122
|
+
lang: 'it',
|
|
123
|
+
chat_id: '42',
|
|
124
|
+
_id: 'xyz',
|
|
125
|
+
user: { id: 24 }
|
|
126
|
+
});
|
|
136
127
|
});
|
|
137
128
|
|
|
138
129
|
it('send outgoing message wrong type', () => {
|
|
@@ -147,44 +138,4 @@ describe('Universal Connector', () => {
|
|
|
147
138
|
.then(() => expect(sendFunction).to.not.have.been.called());
|
|
148
139
|
});
|
|
149
140
|
|
|
150
|
-
it('create a message with just a userId and resolves it', () => {
|
|
151
|
-
return chatServer.start()
|
|
152
|
-
.then(() => chatServer.createMessage(null, '1234', '4567', {
|
|
153
|
-
payload: {
|
|
154
|
-
type: 'a-message-type'
|
|
155
|
-
}
|
|
156
|
-
}))
|
|
157
|
-
.then(message => chatServer.send(message))
|
|
158
|
-
.then(() => expect(sendFunction).to.have.been.called());
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
it('create a message with just a userId and doesn\'t resolve it', () => {
|
|
162
|
-
return chatServer.start()
|
|
163
|
-
.then(() => chatServer.createMessage(null, '5678', '4567', {
|
|
164
|
-
payload: {
|
|
165
|
-
type: 'a-message-type'
|
|
166
|
-
}
|
|
167
|
-
}))
|
|
168
|
-
.then(message => chatServer.send(message))
|
|
169
|
-
.then(
|
|
170
|
-
() => {},
|
|
171
|
-
e => {
|
|
172
|
-
assert.include(e.toString(), 'Error: The userId<->chatId resolver was not able to find a valid chatId for user 5678');
|
|
173
|
-
expect(sendFunction).to.not.have.been.called()
|
|
174
|
-
}
|
|
175
|
-
);
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
it('create a message with a server and just a userId and try to send to the other server', () => {
|
|
179
|
-
return chatServer.start()
|
|
180
|
-
.then(() => chatServer.createMessage(null, '1234', '4567', {
|
|
181
|
-
payload: {
|
|
182
|
-
type: 'a-message-type'
|
|
183
|
-
}
|
|
184
|
-
}))
|
|
185
|
-
.then(message => chatServerAlt.send(message))
|
|
186
|
-
.then(() => expect(sendFunctionAlt).to.have.been.called());
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
|
|
190
141
|
});
|
package/chat-platform.js
CHANGED
|
@@ -2,7 +2,7 @@ const _ = require('underscore');
|
|
|
2
2
|
const _s = require('underscore.string');
|
|
3
3
|
const clc = require('cli-color');
|
|
4
4
|
const prettyjson = require('prettyjson');
|
|
5
|
-
const {
|
|
5
|
+
const { when } = require('./lib/utils');
|
|
6
6
|
const EventEmitter = require('events').EventEmitter;
|
|
7
7
|
const inherits = require('util').inherits;
|
|
8
8
|
const lcd = require('./helpers/lcd');
|
|
@@ -73,15 +73,15 @@ const ChatExpress = function(options) {
|
|
|
73
73
|
// configuration warnings
|
|
74
74
|
if (_.isEmpty(this.options.chatIdKey) && !_.isFunction(this.options.chatIdKey)) {
|
|
75
75
|
// eslint-disable-next-line no-console
|
|
76
|
-
console.log(yellow('WARNING: chatIdKey option is empty'));
|
|
76
|
+
console.log(lcd.timestamp() + yellow('WARNING: chatIdKey option is empty'));
|
|
77
77
|
}
|
|
78
78
|
if (_.isEmpty(this.options.userIdKey) && !_.isFunction(this.options.userIdKey)) {
|
|
79
79
|
// eslint-disable-next-line no-console
|
|
80
|
-
console.log(yellow('WARNING: userIdKey option is empty'));
|
|
80
|
+
console.log(lcd.timestamp() + yellow('WARNING: userIdKey option is empty'));
|
|
81
81
|
}
|
|
82
82
|
if (_.isEmpty(this.options.transport)) {
|
|
83
83
|
// eslint-disable-next-line no-console
|
|
84
|
-
console.log(yellow('WARNING: transport option is empty'));
|
|
84
|
+
console.log(lcd.timestamp() + yellow('WARNING: transport option is empty'));
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
function evaluateParam(payload, newKey, optionKey, chatServer) {
|
|
@@ -96,8 +96,9 @@ const ChatExpress = function(options) {
|
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
function parseMessage(payload, options, chatServer) {
|
|
100
|
-
|
|
99
|
+
async function parseMessage(payload, options, chatServer) {
|
|
100
|
+
const instanceOptions = chatServer.getOptions();
|
|
101
|
+
const { contextProvider, transport } = instanceOptions;
|
|
101
102
|
|
|
102
103
|
payload = _.clone(payload);
|
|
103
104
|
// sets inbound
|
|
@@ -111,21 +112,34 @@ const ChatExpress = function(options) {
|
|
|
111
112
|
}
|
|
112
113
|
|
|
113
114
|
evaluateParam(payload, 'chatId', 'chatIdKey', chatServer);
|
|
114
|
-
evaluateParam(payload, 'userId', 'userIdKey', chatServer);
|
|
115
115
|
evaluateParam(payload, 'messageId', 'messageIdKey', chatServer);
|
|
116
116
|
evaluateParam(payload, 'ts', 'tsKey', chatServer);
|
|
117
117
|
evaluateParam(payload, 'type', 'type', chatServer);
|
|
118
118
|
evaluateParam(payload, 'language', 'language', chatServer);
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
payload.userId = !_.isEmpty(payload.userId) ? payload.userId : payload.chatId;
|
|
119
|
+
evaluateParam(payload, 'userId', 'userIdKey', chatServer);
|
|
120
|
+
|
|
122
121
|
// evaluate callbacks
|
|
123
122
|
const callbacks = chatServer.getCallbacks();
|
|
124
|
-
_(['chatId', '
|
|
123
|
+
_(['chatId', 'ts', 'type', 'language', 'messageId']).each(function(callbackName) {
|
|
125
124
|
if (_.isFunction(callbacks[callbackName])) {
|
|
126
125
|
payload[callbackName] = callbacks[callbackName].call(chatServer, payload)
|
|
127
126
|
}
|
|
128
127
|
});
|
|
128
|
+
|
|
129
|
+
// try to evaluate a default userId
|
|
130
|
+
let defaultUserId = payload.userId;
|
|
131
|
+
if (_.isEmpty(defaultUserId) && _.isFunction(callbacks['userId'])) {
|
|
132
|
+
defaultUserId = callbacks['userId'].call(chatServer, payload);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// get the userId from chatId, ask the context provider to create one and to create
|
|
136
|
+
// the bindings between chatId <-> transport <-> userId
|
|
137
|
+
payload.userId = await contextProvider.getOrCreateUserId(
|
|
138
|
+
String(payload.chatId),
|
|
139
|
+
transport,
|
|
140
|
+
defaultUserId
|
|
141
|
+
);
|
|
142
|
+
|
|
129
143
|
// at this point should have at least the values chatId and type
|
|
130
144
|
if (payload.chatId == null && !options.relaxChatId) {
|
|
131
145
|
throw 'Error: inbound message key "chatId" for transport ' + _this.options.transport + ' is empty\n\n'
|
|
@@ -140,11 +154,11 @@ const ChatExpress = function(options) {
|
|
|
140
154
|
// check if message is null, perhaps someone forgot to resolve a promise
|
|
141
155
|
if (message == null) {
|
|
142
156
|
// eslint-disable-next-line no-console
|
|
143
|
-
console.log(yellow('WARNING: a middleware is returning an empty message'));
|
|
157
|
+
console.log(lcd.timestamp() + yellow('WARNING: a middleware is returning an empty message'));
|
|
144
158
|
}
|
|
145
159
|
if (message.payload == null) {
|
|
146
160
|
// eslint-disable-next-line no-console
|
|
147
|
-
console.log(yellow('WARNING: a middleware is returning an empty payload in message'));
|
|
161
|
+
console.log(lcd.timestamp() + yellow('WARNING: a middleware is returning an empty payload in message'));
|
|
148
162
|
}
|
|
149
163
|
}
|
|
150
164
|
|
|
@@ -166,11 +180,34 @@ const ChatExpress = function(options) {
|
|
|
166
180
|
const onCreateMessage = _.isFunction(options.onCreateMessage) ? options.onCreateMessage : identity;
|
|
167
181
|
inboudMessage = inboudMessage || {};
|
|
168
182
|
|
|
169
|
-
|
|
170
|
-
chatId
|
|
171
|
-
|
|
183
|
+
if (_.isEmpty(userId) && _.isEmpty(chatId)) {
|
|
184
|
+
throw 'Both userId and chatId empty, cannot start a conversation';
|
|
185
|
+
}
|
|
186
|
+
// get the context finding the userId given chatId, chatbotId and transport
|
|
187
|
+
if (_.isEmpty(userId) && !_.isEmpty(chatId)) {
|
|
188
|
+
userId = await contextProvider.getUserId(chatId, options.transport);
|
|
189
|
+
if (_.isEmpty(userId)) {
|
|
190
|
+
throw `Unable to resolve userId from chatId "${chatId}" (${options.transport})`;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
if (_.isEmpty(chatId) && !_.isEmpty(userId)) {
|
|
194
|
+
if (userId === 'simulator') {
|
|
195
|
+
chatId = 0; // for simulators conventionally set zero
|
|
196
|
+
} else {
|
|
197
|
+
chatId = await contextProvider.getChatId(userId, options.transport);
|
|
198
|
+
if (_.isEmpty(chatId)) {
|
|
199
|
+
throw `Unable to resolve chatId from userId "${userId}" (${options.transport}). That means either the userId "${userId}" doesn't exist or it's not possible to find a valid chatId for the platform ${options.transport}.`;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const chatContext = await contextProvider.getOrCreateContext(userId, {
|
|
205
|
+
chatId,
|
|
206
|
+
userId,
|
|
172
207
|
transport: options.transport,
|
|
173
|
-
|
|
208
|
+
chatbotId: options.chatbotId
|
|
209
|
+
});
|
|
210
|
+
|
|
174
211
|
await chatContext.set({
|
|
175
212
|
authorized: false,
|
|
176
213
|
pending: false,
|
|
@@ -180,13 +217,13 @@ const ChatExpress = function(options) {
|
|
|
180
217
|
originalMessage: {
|
|
181
218
|
chatId: chatId,
|
|
182
219
|
userId: userId,
|
|
220
|
+
chatbotId: options.chatbotId,
|
|
183
221
|
messageId: messageId,
|
|
184
222
|
transport: options.transport,
|
|
185
223
|
language: null
|
|
186
224
|
},
|
|
187
225
|
chat() {
|
|
188
|
-
return contextProvider.
|
|
189
|
-
chatId,
|
|
226
|
+
return contextProvider.getContext(
|
|
190
227
|
userId,
|
|
191
228
|
{
|
|
192
229
|
userId: this.originalMessage.userId,
|
|
@@ -214,7 +251,7 @@ const ChatExpress = function(options) {
|
|
|
214
251
|
return onCreateMessage.call(chatServer, message);
|
|
215
252
|
}
|
|
216
253
|
|
|
217
|
-
function inboundMessage(payload, chatServer) {
|
|
254
|
+
async function inboundMessage(payload, chatServer) {
|
|
218
255
|
const options = chatServer.getOptions();
|
|
219
256
|
const contextProvider = options.contextProvider;
|
|
220
257
|
if (chatServer.isDebug()) {
|
|
@@ -233,7 +270,7 @@ const ChatExpress = function(options) {
|
|
|
233
270
|
// parse the message to extract the minimum payload needed for chat-platform to work properly
|
|
234
271
|
// could raise errors, relay to chat server
|
|
235
272
|
try {
|
|
236
|
-
var parsedMessage = parseMessage(payload, _this.options, chatServer);
|
|
273
|
+
var parsedMessage = await parseMessage(payload, _this.options, chatServer);
|
|
237
274
|
} catch(e) {
|
|
238
275
|
chatServer.emit('error', e);
|
|
239
276
|
return;
|
|
@@ -258,10 +295,13 @@ const ChatExpress = function(options) {
|
|
|
258
295
|
inbound: true
|
|
259
296
|
},
|
|
260
297
|
chat: function() {
|
|
261
|
-
return contextProvider.
|
|
262
|
-
parsedMessage.chatId,
|
|
298
|
+
return contextProvider.getContext(
|
|
263
299
|
parsedMessage.userId,
|
|
264
|
-
{
|
|
300
|
+
{
|
|
301
|
+
userId: this.originalMessage.userId,
|
|
302
|
+
transport: this.originalMessage.transport,
|
|
303
|
+
chatId: this.originalMessage.chatId
|
|
304
|
+
}
|
|
265
305
|
);
|
|
266
306
|
},
|
|
267
307
|
api: function() {
|
|
@@ -281,18 +321,23 @@ const ChatExpress = function(options) {
|
|
|
281
321
|
// if any context provider, then create the context
|
|
282
322
|
if (contextProvider != null) {
|
|
283
323
|
stack = stack
|
|
284
|
-
.then(() =>
|
|
285
|
-
parsedMessage.chatId,
|
|
324
|
+
.then(() => contextProvider.getOrCreateContext(
|
|
286
325
|
parsedMessage.userId,
|
|
287
|
-
{
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
326
|
+
{
|
|
327
|
+
userId: parsedMessage.userId,
|
|
328
|
+
transport: parsedMessage.transport,
|
|
329
|
+
chatId: parsedMessage.chatId
|
|
330
|
+
}
|
|
331
|
+
))
|
|
332
|
+
.then(chatContext => when(chatContext.set({
|
|
333
|
+
language: parsedMessage.language,
|
|
334
|
+
authorized: false,
|
|
335
|
+
pending: false
|
|
336
|
+
})))
|
|
337
|
+
.then(() => when(message));
|
|
293
338
|
} else {
|
|
294
339
|
// eslint-disable-next-line no-console
|
|
295
|
-
console.log(yellow('WARNING: context provider was not specified'));
|
|
340
|
+
console.log(lcd.timestamp() + yellow('WARNING: context provider was not specified'));
|
|
296
341
|
}
|
|
297
342
|
// run general middleware
|
|
298
343
|
_(_this.uses.concat(chatServer.getUseMiddleWares())).each(function(filter) {
|
|
@@ -406,18 +451,6 @@ const ChatExpress = function(options) {
|
|
|
406
451
|
return;
|
|
407
452
|
}
|
|
408
453
|
|
|
409
|
-
const instanceOptions = chatServer.getOptions();
|
|
410
|
-
// check if the message is from the right platform (in static class or instance)
|
|
411
|
-
/*if (message.originalMessage != null && message.originalMessage.transport !== _this.options.transport &&
|
|
412
|
-
message.originalMessage.transport !== instanceOptions.transport) {
|
|
413
|
-
// exit, it's not from the current platform
|
|
414
|
-
if (chatServer.isDebug()) {
|
|
415
|
-
// eslint-disable-next-line no-console
|
|
416
|
-
console.log(yellow('Skipped incoming message for platform: ' + message.originalMessage.transport));
|
|
417
|
-
}
|
|
418
|
-
return;
|
|
419
|
-
}*/
|
|
420
|
-
|
|
421
454
|
if (chatServer.isDebug()) {
|
|
422
455
|
// eslint-disable-next-line no-console
|
|
423
456
|
console.log(orange('-- OUTBOUND MESSAGE --'));
|
|
@@ -429,56 +462,7 @@ const ChatExpress = function(options) {
|
|
|
429
462
|
|
|
430
463
|
// create empty promise
|
|
431
464
|
let stack = new Promise(resolve => resolve(message));
|
|
432
|
-
|
|
433
|
-
stack = stack.then(message => {
|
|
434
|
-
const { transport } = instanceOptions;
|
|
435
|
-
if (!isEmpty(message.payload.chatId)) {
|
|
436
|
-
// if there's a chatId, everything ok, skip
|
|
437
|
-
return message;
|
|
438
|
-
} else if (_.isFunction(_globalCallbacks.getChatIdFromUserId) && message.originalMessage.userId != null) {
|
|
439
|
-
// try to call the callback
|
|
440
|
-
try {
|
|
441
|
-
return when(_globalCallbacks.getChatIdFromUserId.call(chatServer, message.originalMessage.userId, transport, message))
|
|
442
|
-
.then(
|
|
443
|
-
chatId => {
|
|
444
|
-
if (isEmpty(chatId)) {
|
|
445
|
-
// eslint-disable-next-line no-console
|
|
446
|
-
console.log(lcd.error(`[onGetChatIdFromUserId] The userId<->chatId resolver was not able to find a valid chatId for user ${message.originalMessage.userId}(${transport})`));
|
|
447
|
-
throw new Error(`The userId<->chatId resolver was not able to find a valid chatId for user ${message.originalMessage.userId}(${transport})`);
|
|
448
|
-
} else {
|
|
449
|
-
if (instanceOptions.debug) {
|
|
450
|
-
// eslint-disable-next-line no-console
|
|
451
|
-
console.log(lcd.green('[onGetChatIdFromUserId]') + lcd.grey(` Resolved ${message.originalMessage.userId}(${transport}) in chatId:${chatId}`));
|
|
452
|
-
}
|
|
453
|
-
message.payload.chatId = chatId;
|
|
454
|
-
return message;
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
);
|
|
458
|
-
} catch(e) {
|
|
459
|
-
// eslint-disable-next-line no-console
|
|
460
|
-
console.log(lcd.error(`[onGetChatIdFromUserId] runtime error in chatId<->userId resolver for user ${message.originalMessage.userId}(${transport})`));
|
|
461
|
-
lcd.dump(e);
|
|
462
|
-
throw new Error(`[onGetChatIdFromUserId] runtime error in chatId<->userId resolver for user ${message.originalMessage.userId}(${transport})`);
|
|
463
|
-
}
|
|
464
|
-
} else {
|
|
465
|
-
// provide some feedback for not being able to resolve a chatId
|
|
466
|
-
if (instanceOptions.debug) {
|
|
467
|
-
if (_.isFunction(_globalCallbacks.getChatIdFromUserId) && isEmpty(message.originalMessage.userId)) {
|
|
468
|
-
// eslint-disable-next-line no-console
|
|
469
|
-
console.log(lcd.warn(`[onGetChatIdFromUserId] Callback was provided but incoming message has no userId (${transport}), unable to resolve a valid chatId`));
|
|
470
|
-
} else if (!_.isFunction(_globalCallbacks.getChatIdFromUserId)) {
|
|
471
|
-
// eslint-disable-next-line no-console
|
|
472
|
-
console.log(lcd.warn('[onGetChatIdFromUserId] Callback was NOT provided and chatId is empty'));
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
// raise error only if not relaxChatId
|
|
476
|
-
if (!instanceOptions.relaxChatId) {
|
|
477
|
-
throw new Error('Incoming message has empty chatId and no chatId - userId resolved has been provided');
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
return message;
|
|
481
|
-
});
|
|
465
|
+
|
|
482
466
|
// run general middleware
|
|
483
467
|
_(_this.uses.concat(chatServer.getUseMiddleWares())).each(function(filter) {
|
|
484
468
|
stack = stack.then(function(message) {
|
|
@@ -624,9 +608,9 @@ const ChatExpress = function(options) {
|
|
|
624
608
|
const uiPort = RED.settings.get('uiPort');
|
|
625
609
|
const options = chatServer.getOptions();
|
|
626
610
|
// eslint-disable-next-line no-console
|
|
627
|
-
console.log('');
|
|
611
|
+
console.log(lcd.timestamp() + '');
|
|
628
612
|
// eslint-disable-next-line no-console
|
|
629
|
-
console.log(grey('------ WebHooks for ' + options.transport.toUpperCase() + '----------------'));
|
|
613
|
+
console.log(lcd.timestamp() + grey('------ WebHooks for ' + options.transport.toUpperCase() + '----------------'));
|
|
630
614
|
_(routes).map((middleware, route) => {
|
|
631
615
|
const host = 'http://localhost' + (uiPort != '80' ? ':' + uiPort : '');
|
|
632
616
|
const callback = generateCallback(route, chatServer);
|
|
@@ -638,14 +622,14 @@ const ChatExpress = function(options) {
|
|
|
638
622
|
description = routesDescription[route].call(chatServer);
|
|
639
623
|
}
|
|
640
624
|
// eslint-disable-next-line no-console
|
|
641
|
-
console.log(green(host + callback) + (description != null ? grey(' - ') + white(description) : ''));
|
|
625
|
+
console.log(lcd.timestamp() + green(host + callback) + (description != null ? grey(' - ') + white(description) : ''));
|
|
642
626
|
// attach to Express instance
|
|
643
627
|
const escaped = String(`^${callback}$`).replace(RegExp('/', 'g'), '\\/');
|
|
644
628
|
RED.httpNode.use(new RegExp(escaped), middleware.bind(chatServer));
|
|
645
629
|
return null;
|
|
646
630
|
});
|
|
647
631
|
// eslint-disable-next-line no-console
|
|
648
|
-
console.log('');
|
|
632
|
+
console.log(lcd.timestamp() + '');
|
|
649
633
|
}
|
|
650
634
|
return when(true);
|
|
651
635
|
}
|
|
@@ -996,7 +980,7 @@ const ChatExpress = function(options) {
|
|
|
996
980
|
.then(function() {
|
|
997
981
|
if (_this.isDebug()) {
|
|
998
982
|
// eslint-disable-next-line no-console
|
|
999
|
-
console.log(green('Chat server started, transport: ') + white(options.transport));
|
|
983
|
+
console.log(green(lcd.timestamp() + 'Chat server started, transport: ') + white(options.transport));
|
|
1000
984
|
}
|
|
1001
985
|
// listen to inbound event
|
|
1002
986
|
var connector = options.connector;
|
|
@@ -1045,7 +1029,7 @@ const ChatExpress = function(options) {
|
|
|
1045
1029
|
} catch(e) {
|
|
1046
1030
|
// todo better error displaying
|
|
1047
1031
|
// eslint-disable-next-line no-console
|
|
1048
|
-
console.log('Error in resolver chatId<->userId', e);
|
|
1032
|
+
console.log(lcd.timestamp() + 'Error in resolver chatId<->userId', e);
|
|
1049
1033
|
throw new Error('Error in resolver chatId<->userId', e);
|
|
1050
1034
|
}
|
|
1051
1035
|
};
|
|
@@ -1061,7 +1045,7 @@ const ChatExpress = function(options) {
|
|
|
1061
1045
|
} catch(e) {
|
|
1062
1046
|
// todo better error displaying
|
|
1063
1047
|
// eslint-disable-next-line no-console
|
|
1064
|
-
console.log('Error in resolver chatId<->preferred transport', e);
|
|
1048
|
+
console.log(lcd.timestamp() + 'Error in resolver chatId<->preferred transport', e);
|
|
1065
1049
|
throw new Error('Error in resolver chatId<->preferred transport', e);
|
|
1066
1050
|
}
|
|
1067
1051
|
};
|
package/helpers/lcd.js
CHANGED
|
@@ -21,6 +21,10 @@ var LCD = {
|
|
|
21
21
|
orange: orange,
|
|
22
22
|
red: red,
|
|
23
23
|
|
|
24
|
+
timestamp() {
|
|
25
|
+
return LCD.white(moment().format('DD MMM HH:mm:ss') + ' - [info] ');
|
|
26
|
+
},
|
|
27
|
+
|
|
24
28
|
dump: function(e, title) {
|
|
25
29
|
this.title(!_.isEmpty(title) ? title : 'Error');
|
|
26
30
|
if (e instanceof Error) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chat-platform",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Universal Chat Platform",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -19,14 +19,15 @@
|
|
|
19
19
|
"prettyjson": "^1.2.1",
|
|
20
20
|
"request": "^2.88.0",
|
|
21
21
|
"sequelize": "^5.21.6",
|
|
22
|
-
"sqlite3": "^
|
|
22
|
+
"sqlite3": "^5.0.0",
|
|
23
23
|
"underscore": "^1.9.1",
|
|
24
24
|
"underscore.string": "^3.3.5"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"chai": "^4.1.1",
|
|
28
28
|
"chai-spies": "^1.0.0",
|
|
29
|
-
"eslint": "^
|
|
29
|
+
"eslint": "^8.12.0",
|
|
30
|
+
"eslint-plugin-jest": "^26.1.3",
|
|
30
31
|
"jest": "^22.0.6"
|
|
31
32
|
}
|
|
32
33
|
}
|
package/providers/memory.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
const _ = require('underscore');
|
|
2
|
-
const _store = {};
|
|
3
|
-
const _storeUserIds = {};
|
|
4
2
|
|
|
5
3
|
const isEmpty = value => value == null || value === '';
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
const _storeChatIds = {};
|
|
6
|
+
const _storeUserIds = {};
|
|
7
|
+
const _store = {};
|
|
8
|
+
|
|
9
|
+
function MemoryStore(userId, statics = {}, warnings = false) {
|
|
10
|
+
//this.chatId = chatId != null ? String(chatId) : null;
|
|
9
11
|
this.userId = userId != null ? String(userId) : null;
|
|
10
12
|
// make sure userId is always a string
|
|
11
13
|
this.statics = Object.assign({}, statics, { userId: statics.userId != null ? String(statics.userId) : undefined });
|
|
@@ -14,19 +16,15 @@ function MemoryStore(chatId, userId, statics = {}, warnings = false) {
|
|
|
14
16
|
}
|
|
15
17
|
return this;
|
|
16
18
|
}
|
|
19
|
+
|
|
17
20
|
_.extend(MemoryStore.prototype, {
|
|
18
21
|
getPayload() {
|
|
19
22
|
// always precedence to userId
|
|
20
23
|
if (this.userId != null && _storeUserIds[this.userId] != null) {
|
|
21
24
|
return _storeUserIds[this.userId];
|
|
22
|
-
} else if (this.chatId != null && _store[this.chatId] != null) {
|
|
23
|
-
return _store[this.chatId];
|
|
24
25
|
} else if (this.userId != null) {
|
|
25
26
|
_storeUserIds[this.userId] = {};
|
|
26
27
|
return _storeUserIds[this.userId];
|
|
27
|
-
} else if (this.chatId != null) {
|
|
28
|
-
_store[this.chatId] = {};
|
|
29
|
-
return _store[this.chatId];
|
|
30
28
|
}
|
|
31
29
|
return {};
|
|
32
30
|
},
|
|
@@ -105,32 +103,74 @@ _.extend(MemoryStore.prototype, {
|
|
|
105
103
|
|
|
106
104
|
function MemoryFactory() {
|
|
107
105
|
|
|
106
|
+
this.getOrCreateUserId = async function(chatId, transport, defaultUserId) {
|
|
107
|
+
// try to find a userId given then chatId / transport for the current chatbot
|
|
108
|
+
if (isEmpty(chatId)) {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (_storeChatIds[transport] != null && !_.isEmpty(_storeChatIds[transport][chatId])) {
|
|
113
|
+
return _storeUserIds[transport][chatId];
|
|
114
|
+
} else {
|
|
115
|
+
const userId = !_.isEmpty(defaultUserId) ? defaultUserId : chatId;
|
|
116
|
+
_storeUserIds[chatId] = _storeUserIds[chatId] != null ? _storeUserIds[chatId] : {};
|
|
117
|
+
_storeUserIds[chatId][transport] = userId;
|
|
118
|
+
_storeChatIds[userId] = _storeChatIds[userId] != null ? _storeChatIds[userId] : {};
|
|
119
|
+
_storeChatIds[userId][transport] = chatId;
|
|
120
|
+
return userId;
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
this.getUserId = async function(chatId, transport) {
|
|
125
|
+
return _storeUserIds[chatId] != null && !_.isEmpty(_storeUserIds[chatId][transport]) ?
|
|
126
|
+
_storeUserIds[chatId][transport] : chatId;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
this.getChatId = async function(userId, transport) {
|
|
130
|
+
return _storeChatIds[userId] != null && !_.isEmpty(_storeChatIds[userId][transport]) ?
|
|
131
|
+
_storeChatIds[userId][transport] : null;
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
this.getOrCreateContext = async function(userId, statics) {
|
|
135
|
+
if (isEmpty(userId)) {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
return new MemoryStore(userId, { ...statics });
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
this.mergeUserId = async function(fromUserId, toUserId) {
|
|
142
|
+
const destination = _storeChatIds[toUserId];
|
|
143
|
+
const source = _storeChatIds[fromUserId];
|
|
144
|
+
|
|
145
|
+
Object.keys(source)
|
|
146
|
+
.forEach(transport => {
|
|
147
|
+
const chatId = source[transport];
|
|
148
|
+
// copy chatId/transport to destination if doesn't exist
|
|
149
|
+
if (destination[transport] == null) {
|
|
150
|
+
destination[transport] = chatId;
|
|
151
|
+
}
|
|
152
|
+
// replace chatId reference
|
|
153
|
+
_storeUserIds[chatId][transport] = toUserId;
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
delete _storeChatIds[fromUserId];
|
|
157
|
+
};
|
|
158
|
+
this.getContext = function(userId, statics) {
|
|
159
|
+
return new MemoryStore(userId, { ...statics });
|
|
160
|
+
};
|
|
161
|
+
|
|
108
162
|
this.getOrCreate = function(chatId, userId, statics) {
|
|
163
|
+
console.log('getOrCreate() is deprecated, use getOrCreateContext() instead');
|
|
109
164
|
if (isEmpty(chatId) && isEmpty(userId)) {
|
|
110
165
|
return null;
|
|
111
166
|
}
|
|
112
167
|
// just create an class that just wraps chatId and userId, add static value (cline)
|
|
113
|
-
const store = new MemoryStore(
|
|
168
|
+
const store = new MemoryStore(userId, { ...statics });
|
|
114
169
|
return store;
|
|
115
|
-
/*const chatContext = this.get(chatId, userId);
|
|
116
|
-
if (chatContext == null) {
|
|
117
|
-
const memoryStore = new MemoryStore({ ...defaults });
|
|
118
|
-
_store[chatId] = memoryStore;
|
|
119
|
-
if (!isEmpty(userId)) {
|
|
120
|
-
_storeUserIds[userId] = memoryStore;
|
|
121
|
-
}
|
|
122
|
-
return _store[chatId];
|
|
123
|
-
}
|
|
124
|
-
return chatContext;
|
|
125
|
-
*/
|
|
126
170
|
};
|
|
171
|
+
|
|
127
172
|
this.get = function(chatId, userId, statics) {
|
|
128
|
-
|
|
129
|
-
return _store[chatId];
|
|
130
|
-
} else if (!isEmpty(userId) && _storeUserIds[userId] != null) {
|
|
131
|
-
return _storeUserIds[userId];
|
|
132
|
-
}
|
|
133
|
-
return null;*/
|
|
173
|
+
console.log('get() is deprecated');
|
|
134
174
|
return new MemoryStore(chatId, userId, { ...statics });
|
|
135
175
|
};
|
|
136
176
|
|
|
@@ -145,13 +185,13 @@ _.extend(MemoryFactory.prototype, {
|
|
|
145
185
|
getOrCreate: function(/*chatId, userId, statics*/) {
|
|
146
186
|
},
|
|
147
187
|
assignToUser(userId, context) {
|
|
148
|
-
|
|
149
|
-
// TODO perhaps remove other occurence
|
|
188
|
+
console.log('assignToUser() is deprecated')
|
|
150
189
|
_storeUserIds[userId] = context;
|
|
151
190
|
},
|
|
152
191
|
reset() {
|
|
153
192
|
Object.keys(_store).forEach(key => delete _store[key]);
|
|
154
193
|
Object.keys(_storeUserIds).forEach(key => delete _storeUserIds[key]);
|
|
194
|
+
Object.keys(_storeChatIds).forEach(key => delete _storeUserIds[key]);
|
|
155
195
|
return this;
|
|
156
196
|
},
|
|
157
197
|
stop: function() {
|