chat-platform 1.3.1 → 2.0.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/.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 +74 -104
- 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,30 @@ 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
|
+
chatId = await contextProvider.getChatId(userId, options.transport);
|
|
195
|
+
if (_.isEmpty(chatId)) {
|
|
196
|
+
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}.`;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const chatContext = await contextProvider.getOrCreateContext(userId, {
|
|
201
|
+
chatId,
|
|
202
|
+
userId,
|
|
172
203
|
transport: options.transport,
|
|
173
|
-
|
|
204
|
+
chatbotId: options.chatbotId
|
|
205
|
+
});
|
|
206
|
+
|
|
174
207
|
await chatContext.set({
|
|
175
208
|
authorized: false,
|
|
176
209
|
pending: false,
|
|
@@ -180,13 +213,13 @@ const ChatExpress = function(options) {
|
|
|
180
213
|
originalMessage: {
|
|
181
214
|
chatId: chatId,
|
|
182
215
|
userId: userId,
|
|
216
|
+
chatbotId: options.chatbotId,
|
|
183
217
|
messageId: messageId,
|
|
184
218
|
transport: options.transport,
|
|
185
219
|
language: null
|
|
186
220
|
},
|
|
187
221
|
chat() {
|
|
188
|
-
return contextProvider.
|
|
189
|
-
chatId,
|
|
222
|
+
return contextProvider.getContext(
|
|
190
223
|
userId,
|
|
191
224
|
{
|
|
192
225
|
userId: this.originalMessage.userId,
|
|
@@ -214,7 +247,7 @@ const ChatExpress = function(options) {
|
|
|
214
247
|
return onCreateMessage.call(chatServer, message);
|
|
215
248
|
}
|
|
216
249
|
|
|
217
|
-
function inboundMessage(payload, chatServer) {
|
|
250
|
+
async function inboundMessage(payload, chatServer) {
|
|
218
251
|
const options = chatServer.getOptions();
|
|
219
252
|
const contextProvider = options.contextProvider;
|
|
220
253
|
if (chatServer.isDebug()) {
|
|
@@ -233,7 +266,7 @@ const ChatExpress = function(options) {
|
|
|
233
266
|
// parse the message to extract the minimum payload needed for chat-platform to work properly
|
|
234
267
|
// could raise errors, relay to chat server
|
|
235
268
|
try {
|
|
236
|
-
var parsedMessage = parseMessage(payload, _this.options, chatServer);
|
|
269
|
+
var parsedMessage = await parseMessage(payload, _this.options, chatServer);
|
|
237
270
|
} catch(e) {
|
|
238
271
|
chatServer.emit('error', e);
|
|
239
272
|
return;
|
|
@@ -258,14 +291,12 @@ const ChatExpress = function(options) {
|
|
|
258
291
|
inbound: true
|
|
259
292
|
},
|
|
260
293
|
chat: function() {
|
|
261
|
-
return contextProvider.
|
|
262
|
-
parsedMessage.chatId,
|
|
294
|
+
return contextProvider.getContext(
|
|
263
295
|
parsedMessage.userId,
|
|
264
296
|
{
|
|
265
297
|
userId: this.originalMessage.userId,
|
|
266
298
|
transport: this.originalMessage.transport,
|
|
267
|
-
chatId: this.originalMessage.chatId
|
|
268
|
-
chatbotId: this.originalMessage.chatbotId
|
|
299
|
+
chatId: this.originalMessage.chatId
|
|
269
300
|
}
|
|
270
301
|
);
|
|
271
302
|
},
|
|
@@ -286,23 +317,23 @@ const ChatExpress = function(options) {
|
|
|
286
317
|
// if any context provider, then create the context
|
|
287
318
|
if (contextProvider != null) {
|
|
288
319
|
stack = stack
|
|
289
|
-
.then(() =>
|
|
290
|
-
parsedMessage.chatId,
|
|
320
|
+
.then(() => contextProvider.getOrCreateContext(
|
|
291
321
|
parsedMessage.userId,
|
|
292
322
|
{
|
|
293
323
|
userId: parsedMessage.userId,
|
|
294
324
|
transport: parsedMessage.transport,
|
|
295
|
-
chatId: parsedMessage.chatId
|
|
296
|
-
chatbotId: options.chatbotId
|
|
325
|
+
chatId: parsedMessage.chatId
|
|
297
326
|
}
|
|
298
|
-
))
|
|
299
|
-
.then(chatContext => when(chatContext.set({
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
327
|
+
))
|
|
328
|
+
.then(chatContext => when(chatContext.set({
|
|
329
|
+
language: parsedMessage.language,
|
|
330
|
+
authorized: false,
|
|
331
|
+
pending: false
|
|
332
|
+
})))
|
|
333
|
+
.then(() => when(message));
|
|
303
334
|
} else {
|
|
304
335
|
// eslint-disable-next-line no-console
|
|
305
|
-
console.log(yellow('WARNING: context provider was not specified'));
|
|
336
|
+
console.log(lcd.timestamp() + yellow('WARNING: context provider was not specified'));
|
|
306
337
|
}
|
|
307
338
|
// run general middleware
|
|
308
339
|
_(_this.uses.concat(chatServer.getUseMiddleWares())).each(function(filter) {
|
|
@@ -416,18 +447,6 @@ const ChatExpress = function(options) {
|
|
|
416
447
|
return;
|
|
417
448
|
}
|
|
418
449
|
|
|
419
|
-
const instanceOptions = chatServer.getOptions();
|
|
420
|
-
// check if the message is from the right platform (in static class or instance)
|
|
421
|
-
/*if (message.originalMessage != null && message.originalMessage.transport !== _this.options.transport &&
|
|
422
|
-
message.originalMessage.transport !== instanceOptions.transport) {
|
|
423
|
-
// exit, it's not from the current platform
|
|
424
|
-
if (chatServer.isDebug()) {
|
|
425
|
-
// eslint-disable-next-line no-console
|
|
426
|
-
console.log(yellow('Skipped incoming message for platform: ' + message.originalMessage.transport));
|
|
427
|
-
}
|
|
428
|
-
return;
|
|
429
|
-
}*/
|
|
430
|
-
|
|
431
450
|
if (chatServer.isDebug()) {
|
|
432
451
|
// eslint-disable-next-line no-console
|
|
433
452
|
console.log(orange('-- OUTBOUND MESSAGE --'));
|
|
@@ -439,56 +458,7 @@ const ChatExpress = function(options) {
|
|
|
439
458
|
|
|
440
459
|
// create empty promise
|
|
441
460
|
let stack = new Promise(resolve => resolve(message));
|
|
442
|
-
|
|
443
|
-
stack = stack.then(message => {
|
|
444
|
-
const { transport } = instanceOptions;
|
|
445
|
-
if (!isEmpty(message.payload.chatId)) {
|
|
446
|
-
// if there's a chatId, everything ok, skip
|
|
447
|
-
return message;
|
|
448
|
-
} else if (_.isFunction(_globalCallbacks.getChatIdFromUserId) && message.originalMessage.userId != null) {
|
|
449
|
-
// try to call the callback
|
|
450
|
-
try {
|
|
451
|
-
return when(_globalCallbacks.getChatIdFromUserId.call(chatServer, message.originalMessage.userId, transport, message))
|
|
452
|
-
.then(
|
|
453
|
-
chatId => {
|
|
454
|
-
if (isEmpty(chatId)) {
|
|
455
|
-
// eslint-disable-next-line no-console
|
|
456
|
-
console.log(lcd.error(`[onGetChatIdFromUserId] The userId<->chatId resolver was not able to find a valid chatId for user ${message.originalMessage.userId}(${transport})`));
|
|
457
|
-
throw new Error(`The userId<->chatId resolver was not able to find a valid chatId for user ${message.originalMessage.userId}(${transport})`);
|
|
458
|
-
} else {
|
|
459
|
-
if (instanceOptions.debug) {
|
|
460
|
-
// eslint-disable-next-line no-console
|
|
461
|
-
console.log(lcd.green('[onGetChatIdFromUserId]') + lcd.grey(` Resolved ${message.originalMessage.userId}(${transport}) in chatId:${chatId}`));
|
|
462
|
-
}
|
|
463
|
-
message.payload.chatId = chatId;
|
|
464
|
-
return message;
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
);
|
|
468
|
-
} catch(e) {
|
|
469
|
-
// eslint-disable-next-line no-console
|
|
470
|
-
console.log(lcd.error(`[onGetChatIdFromUserId] runtime error in chatId<->userId resolver for user ${message.originalMessage.userId}(${transport})`));
|
|
471
|
-
lcd.dump(e);
|
|
472
|
-
throw new Error(`[onGetChatIdFromUserId] runtime error in chatId<->userId resolver for user ${message.originalMessage.userId}(${transport})`);
|
|
473
|
-
}
|
|
474
|
-
} else {
|
|
475
|
-
// provide some feedback for not being able to resolve a chatId
|
|
476
|
-
if (instanceOptions.debug) {
|
|
477
|
-
if (_.isFunction(_globalCallbacks.getChatIdFromUserId) && isEmpty(message.originalMessage.userId)) {
|
|
478
|
-
// eslint-disable-next-line no-console
|
|
479
|
-
console.log(lcd.warn(`[onGetChatIdFromUserId] Callback was provided but incoming message has no userId (${transport}), unable to resolve a valid chatId`));
|
|
480
|
-
} else if (!_.isFunction(_globalCallbacks.getChatIdFromUserId)) {
|
|
481
|
-
// eslint-disable-next-line no-console
|
|
482
|
-
console.log(lcd.warn('[onGetChatIdFromUserId] Callback was NOT provided and chatId is empty'));
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
// raise error only if not relaxChatId
|
|
486
|
-
if (!instanceOptions.relaxChatId) {
|
|
487
|
-
throw new Error('Incoming message has empty chatId and no chatId - userId resolved has been provided');
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
return message;
|
|
491
|
-
});
|
|
461
|
+
|
|
492
462
|
// run general middleware
|
|
493
463
|
_(_this.uses.concat(chatServer.getUseMiddleWares())).each(function(filter) {
|
|
494
464
|
stack = stack.then(function(message) {
|
|
@@ -634,9 +604,9 @@ const ChatExpress = function(options) {
|
|
|
634
604
|
const uiPort = RED.settings.get('uiPort');
|
|
635
605
|
const options = chatServer.getOptions();
|
|
636
606
|
// eslint-disable-next-line no-console
|
|
637
|
-
console.log('');
|
|
607
|
+
console.log(lcd.timestamp() + '');
|
|
638
608
|
// eslint-disable-next-line no-console
|
|
639
|
-
console.log(grey('------ WebHooks for ' + options.transport.toUpperCase() + '----------------'));
|
|
609
|
+
console.log(lcd.timestamp() + grey('------ WebHooks for ' + options.transport.toUpperCase() + '----------------'));
|
|
640
610
|
_(routes).map((middleware, route) => {
|
|
641
611
|
const host = 'http://localhost' + (uiPort != '80' ? ':' + uiPort : '');
|
|
642
612
|
const callback = generateCallback(route, chatServer);
|
|
@@ -648,14 +618,14 @@ const ChatExpress = function(options) {
|
|
|
648
618
|
description = routesDescription[route].call(chatServer);
|
|
649
619
|
}
|
|
650
620
|
// eslint-disable-next-line no-console
|
|
651
|
-
console.log(green(host + callback) + (description != null ? grey(' - ') + white(description) : ''));
|
|
621
|
+
console.log(lcd.timestamp() + green(host + callback) + (description != null ? grey(' - ') + white(description) : ''));
|
|
652
622
|
// attach to Express instance
|
|
653
623
|
const escaped = String(`^${callback}$`).replace(RegExp('/', 'g'), '\\/');
|
|
654
624
|
RED.httpNode.use(new RegExp(escaped), middleware.bind(chatServer));
|
|
655
625
|
return null;
|
|
656
626
|
});
|
|
657
627
|
// eslint-disable-next-line no-console
|
|
658
|
-
console.log('');
|
|
628
|
+
console.log(lcd.timestamp() + '');
|
|
659
629
|
}
|
|
660
630
|
return when(true);
|
|
661
631
|
}
|
|
@@ -1006,7 +976,7 @@ const ChatExpress = function(options) {
|
|
|
1006
976
|
.then(function() {
|
|
1007
977
|
if (_this.isDebug()) {
|
|
1008
978
|
// eslint-disable-next-line no-console
|
|
1009
|
-
console.log(green('Chat server started, transport: ') + white(options.transport));
|
|
979
|
+
console.log(green(lcd.timestamp() + 'Chat server started, transport: ') + white(options.transport));
|
|
1010
980
|
}
|
|
1011
981
|
// listen to inbound event
|
|
1012
982
|
var connector = options.connector;
|
|
@@ -1055,7 +1025,7 @@ const ChatExpress = function(options) {
|
|
|
1055
1025
|
} catch(e) {
|
|
1056
1026
|
// todo better error displaying
|
|
1057
1027
|
// eslint-disable-next-line no-console
|
|
1058
|
-
console.log('Error in resolver chatId<->userId', e);
|
|
1028
|
+
console.log(lcd.timestamp() + 'Error in resolver chatId<->userId', e);
|
|
1059
1029
|
throw new Error('Error in resolver chatId<->userId', e);
|
|
1060
1030
|
}
|
|
1061
1031
|
};
|
|
@@ -1071,7 +1041,7 @@ const ChatExpress = function(options) {
|
|
|
1071
1041
|
} catch(e) {
|
|
1072
1042
|
// todo better error displaying
|
|
1073
1043
|
// eslint-disable-next-line no-console
|
|
1074
|
-
console.log('Error in resolver chatId<->preferred transport', e);
|
|
1044
|
+
console.log(lcd.timestamp() + 'Error in resolver chatId<->preferred transport', e);
|
|
1075
1045
|
throw new Error('Error in resolver chatId<->preferred transport', e);
|
|
1076
1046
|
}
|
|
1077
1047
|
};
|
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": "
|
|
3
|
+
"version": "2.0.1",
|
|
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() {
|