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.
@@ -82,57 +82,48 @@ describe('Universal Connector', () => {
82
82
  resolve(message);
83
83
  })
84
84
  );
85
- // store the global resolver
86
- chatServer.onGetChatIdFromUserId((userId, transport, message) => {
87
- if (userId === '1234' && transport === 'universal') {
88
- return '42';
89
- } else if (userId === '1234' && transport === 'universal-alt') {
90
- return '43';
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('send outgoing message', () => {
124
- return chatServer.start()
125
- .then(() => chatServer.createMessage('42', '1234', '4567', {
126
- payload: {
127
- type: 'a-message-type',
128
- chatId: '42'
129
- }
130
- }))
131
- .then(message => chatServer.send(message))
132
- .then(message => {
133
- assert.equal(message.message_id, '444');
134
- expect(sendFunction).to.have.been.called();
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 { isEmpty, when } = require('./lib/utils');
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
- var instanceOptions = chatServer.getOptions();
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
- // default userId on chatId, never leave it blank, pay attention
120
- // on removing this constraint (some chat context may be lost)
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', 'userId', 'ts', 'type', 'language', 'messageId']).each(function(callbackName) {
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
- const chatContext = await when(contextProvider.getOrCreate(chatId, userId, {
170
- chatId: chatId,
171
- userId: userId,
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.get(
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.get(
262
- parsedMessage.chatId,
298
+ return contextProvider.getContext(
263
299
  parsedMessage.userId,
264
- { userId: this.originalMessage.userId, transport: this.originalMessage.transport, chatId: this.originalMessage.chatId }
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(() => when(contextProvider.getOrCreate(
285
- parsedMessage.chatId,
324
+ .then(() => contextProvider.getOrCreateContext(
286
325
  parsedMessage.userId,
287
- { userId: parsedMessage.userId, transport: parsedMessage.transport, chatId: parsedMessage.chatId }
288
- )))
289
- .then(chatContext => when(chatContext.set({ language: parsedMessage.language, authorized: false, pending: false })))
290
- .then(function() {
291
- return when(message);
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
- // check for chatId, if not present check for userId-chatId translator, otherwise fail
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.2",
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": "^4.1.1",
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": "^4.12.1",
29
+ "eslint": "^8.12.0",
30
+ "eslint-plugin-jest": "^26.1.3",
30
31
  "jest": "^22.0.6"
31
32
  }
32
33
  }
@@ -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
- function MemoryStore(chatId, userId, statics = {}, warnings = false) {
8
- this.chatId = chatId != null ? String(chatId) : null;
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(chatId, userId, { ...statics });
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
- /*if (!isEmpty(chatId) && _store[chatId] != null) {
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
- // when merging a user into another, this trasnfer the current context to another user
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() {