chat-platform 1.3.2 → 2.0.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.
@@ -10,16 +10,16 @@ const Op = Sequelize.Op;
10
10
 
11
11
  const isEmpty = value => value == null || value === '';
12
12
 
13
-
14
-
15
-
16
- function SQLiteStore(chatId, userId, statics = {}, warnings = false) {
17
- this.chatId = chatId != null ? String(chatId) : null;
13
+ function SQLiteStore(userId, chatbotId, statics = {}, warnings = false) {
18
14
  this.userId = userId != null ? String(userId) : null;
15
+ this.chatbotId = chatbotId;
19
16
  // make sure userId is always a string
20
17
  this.statics = Object.assign({}, statics, { userId: statics.userId != null ? String(statics.userId) : undefined });
21
18
  if (warnings && _.isEmpty(statics)) {
22
- console.trace('Warning: empty statics vars')
19
+ console.trace('Warning: empty statics vars');
20
+ }
21
+ if (warnings && _.isEmpty(chatbotId)) {
22
+ console.trace('Warning: empty chatbotId');
23
23
  }
24
24
  return this;
25
25
  }
@@ -30,6 +30,9 @@ function SQLiteFactory(params) {
30
30
  if (_.isEmpty(params.dbPath)) {
31
31
  throw 'SQLite context provider: missing parameter "dbPath"';
32
32
  }
33
+ if (_.isEmpty(params.chatbotId)) {
34
+ throw 'SQLite context provider: missing parameter "chatbotId"';
35
+ }
33
36
  if (!fs.existsSync(params.dbPath)) {
34
37
  //throw 'SQLite context provider: "dbPath" (' + params.path + ') doesn\'t exist';
35
38
  try {
@@ -39,6 +42,7 @@ function SQLiteFactory(params) {
39
42
  throw 'SQLite context provider: "dbPath" (' + params.path + ') doesn\'t exist and unable to create';
40
43
  }
41
44
  }
45
+ this.chatbotId = params.chatbotId;
42
46
 
43
47
  const sequelize = new Sequelize('mission_control', '', '', {
44
48
  host: 'localhost',
@@ -49,16 +53,28 @@ function SQLiteFactory(params) {
49
53
 
50
54
  const Context = sequelize.define('context', {
51
55
  userId: { type: Sequelize.STRING },
52
- chatId: { type: Sequelize.STRING },
53
- payload: { type: Sequelize.STRING }
56
+ payload: { type: Sequelize.STRING },
57
+ chatbotId: { type: Sequelize.STRING }
54
58
  }, {
55
59
  indexes: [
56
- { name: 'chatid_userid', using: 'BTREE', fields: ['userId'] },
57
- { name: 'chatid_chatid', using: 'BTREE', fields: ['chatId'] }
60
+ { name: 'context_userid', using: 'BTREE', fields: ['userId'] },
61
+ { name: 'context_chatbotId', using: 'BTREE', fields: ['chatbotId'] }
58
62
  ]
59
63
  });
60
64
 
61
-
65
+ const ChatId = sequelize.define('chatid', {
66
+ userId: { type: Sequelize.STRING, allowNull: false },
67
+ chatId: { type: Sequelize.STRING, allowNull: false },
68
+ transport: { type: Sequelize.STRING, allowNull: false },
69
+ chatbotId: { type: Sequelize.TEXT }
70
+ }, {
71
+ indexes: [
72
+ { name: 'chatid_userid', using: 'BTREE', fields: ['userId'] },
73
+ { name: 'chatid_chatid', using: 'BTREE', fields: ['chatId'] },
74
+ { name: 'chatid_transport', using: 'BTREE', fields: ['transport'] },
75
+ { name: 'chatid_chatbotId', using: 'BTREE', fields: ['chatbotId'] }
76
+ ]
77
+ });
62
78
 
63
79
  // **
64
80
  // Start definition if SQLite store, with closure I can spare passing a Context as configuration
@@ -97,30 +113,14 @@ function SQLiteFactory(params) {
97
113
  },
98
114
 
99
115
  async getPayload() {
100
- // get payload using chatId or userId
101
- const contexts = await Context.findAll({ where: {
102
- [Op.or]: [
103
- { chatId: this.chatId },
104
- { userId: this.userId }
105
- ]
116
+ // get payload using userId (it generally defaults to chatId)
117
+ const context = await Context.findOne({ where: {
118
+ userId: this.userId,
119
+ chatbotId: this.chatbotId
106
120
  }});
107
- let payload;
108
- let context;
109
- if (contexts.length === 0) {
110
- // if not present then create the row
111
- context = await Context.create({ payload: JSON.stringify({}), chatId: this.chatId, userId: this.userId });
112
- payload = {};
113
- } else {
114
- // if by any change there are two matched rows, one for the chatId and one for userId
115
- // always prefer the userId (that could happen if the user star using the sqlite provider) as
116
- // is and at some point the MC_store assign the context to the user
117
- context = contexts.find(context => context.userId === this.userId);
118
- if (context == null) {
119
- context = contexts.find(context => context.chatId === this.chatId);
120
- }
121
- if (context == null) {
122
- context = contexts[0];
123
- }
121
+
122
+ if (context != null) {
123
+ let payload;
124
124
  // finally decode
125
125
  try {
126
126
  payload = JSON.parse(context.payload);
@@ -128,8 +128,16 @@ function SQLiteFactory(params) {
128
128
  // default if error
129
129
  payload = {};
130
130
  }
131
+ return { payload, id: context.id };
132
+ } else {
133
+ // if not present then create the row
134
+ const context = await Context.create({
135
+ payload: JSON.stringify({}),
136
+ chatbotId: this.chatbotId,
137
+ userId: this.userId
138
+ });
139
+ return { payload: {}, id: context.id };
131
140
  }
132
- return { payload, id: context.id };
133
141
  },
134
142
 
135
143
  async set(key, value) {
@@ -168,20 +176,82 @@ function SQLiteFactory(params) {
168
176
  return this;
169
177
  }
170
178
  });
179
+
171
180
  // **
172
181
  // End definition if SQLite store
173
182
  // **
174
183
 
184
+ this.getOrCreateUserId = async function(chatId, transport, defaultUserId) {
185
+ // try to find a userId given then chatId / transport for the current chatbot
186
+ const binding = await ChatId.findOne({
187
+ where: { chatId, transport, chatbotId: this.chatbotId }
188
+ });
189
+
190
+ if (binding != null) {
191
+ return binding.userId;
192
+ } else {
193
+ const userId = !_.isEmpty(defaultUserId) ? defaultUserId : chatId;
194
+ await ChatId.create({ chatId, transport, chatbotId: this.chatbotId, userId });
195
+ return userId;
196
+ }
197
+ };
198
+
199
+ /**
200
+ * getUserId
201
+ * Get the userId from chatId, if doesn't exist, defaults to chatId
202
+ */
203
+ this.getUserId = async function(chatId, transport) {
204
+ const binding = await ChatId.findOne({
205
+ where: { chatId, transport, chatbotId: this.chatbotId }
206
+ });
207
+ return binding != null ? binding.userId : chatId;
208
+ };
209
+
210
+ this.getChatId = async function(userId, transport) {
211
+ const binding = await ChatId.findOne({
212
+ where: { userId, transport, chatbotId: this.chatbotId }
213
+ });
214
+ return binding != null ? binding.chatId : null;
215
+ };
216
+
217
+ this.getOrCreateContext = async function(userId, statics) {
218
+ if (isEmpty(userId)) {
219
+ return null;
220
+ }
221
+ // just create an class that just wraps userId, chatbotId and static value (cline)
222
+ const store = new SQLiteStore(userId, this.chatbotId, { ...statics });
223
+ return store;
224
+ };
225
+
226
+ this.mergeUserId = async function(fromUserId, toUserId) {
227
+ const fromChatIds = await ChatId.findAll({ where: { userId: fromUserId, chatbotId: this.chatbotId }});
228
+ const toChatIds = await ChatId.findAll({ where: { userId: toUserId, chatbotId: this.chatbotId }});
229
+
230
+ // turn only chatIds that don't already exists
231
+ for (const item of fromChatIds) {
232
+ const hasTransport = toChatIds.filter(({ transport }) => transport === item.transport).length !== 0;
233
+ if (!hasTransport) {
234
+ await ChatId.update({ userId: toUserId }, { where: { id: item.id }});
235
+ }
236
+ }
237
+ };
238
+
239
+ // deprecated below
240
+
175
241
  this.getOrCreate = async function(chatId, userId, statics) {
176
242
  if (isEmpty(chatId) && isEmpty(userId)) {
177
243
  return null;
178
244
  }
179
- // just create an class that just wraps chatId and userId, add static value (cline)
180
- const store = new SQLiteStore(chatId, userId, { ...statics });
245
+ // just create an class that just wraps userId, chatbotId and static value (cline)
246
+ const store = new SQLiteStore(userId, this.chatbotId, { ...statics });
181
247
  return store;
182
248
  };
183
249
  this.get = function(chatId, userId, statics) {
184
- return new SQLiteStore(chatId, userId, statics);
250
+ console.warn('This is deprectated, use getContext instead')
251
+ return new SQLiteStore(userId, this.chatbotId, statics);
252
+ };
253
+ this.getContext = function(userId, statics) {
254
+ return new SQLiteStore(userId, this.chatbotId, statics);
185
255
  };
186
256
  this.assignToUser = async (userId, context) => {
187
257
  };
@@ -213,6 +283,7 @@ function SQLiteFactory(params) {
213
283
  try {
214
284
  if (createTable) {
215
285
  await Context.sync();
286
+ await ChatId.sync();
216
287
  }
217
288
  // then log, don't move, keep the log lines together
218
289
  console.log(lcd.timestamp() + 'SQLite context provider configuration:');
@@ -263,5 +334,4 @@ _.extend(SQLiteFactory.prototype, {
263
334
  }
264
335
  });
265
336
 
266
-
267
337
  module.exports = SQLiteFactory;
@@ -1,174 +0,0 @@
1
- const _ = require('underscore');
2
- const assert = require('chai').assert;
3
- const RED = require('../lib/red-stub')();
4
- const ContextProviders = require('../chat-context-factory');
5
- const os = require('os');
6
- const fs = require('fs');
7
- const { when } = require('../lib/utils');
8
-
9
-
10
- describe('Chat context provider file', () => {
11
-
12
- const contextProviders = ContextProviders(RED);
13
- //const path = os.tmpdir();
14
- const path = __dirname + '/test-files';
15
- const provider = contextProviders.getProvider('plain-file', { path });
16
-
17
- beforeEach(() => {
18
- return provider.reset({ path });
19
- });
20
-
21
- afterAll(() => {
22
- return provider.reset({ path });
23
- });
24
-
25
- it('should create a context provider with some default params', () => {
26
-
27
- assert.isTrue(contextProviders.hasProvider('plain-file'));
28
- const provider = contextProviders.getProvider('plain-file', { path });
29
- assert.isFunction(provider.getOrCreate);
30
- assert.isFunction(provider.get);
31
-
32
- return when(provider.getOrCreate(42, null, { myVariable: 'initial value'}))
33
- .then(chatContext => {
34
- assert.isFunction(chatContext.get);
35
- assert.isFunction(chatContext.set);
36
- assert.isFunction(chatContext.all);
37
- assert.isFunction(chatContext.remove);
38
- assert.isFunction(chatContext.clear);
39
- return chatContext.get('myVariable');
40
- }).then(myVariable => {
41
- assert.equal(myVariable, 'initial value');
42
- // assert.isTrue(fs.existsSync(`${path}/store-7369f3c86bf3c0a354615432832d9e8f.json`));
43
- });
44
- });
45
-
46
- it('should set some value and then get and remove it', async () => {
47
- const chatContext = await provider.getOrCreate(42, null, {});
48
- await chatContext.set('firstName', 'Guidone');
49
- let firstName =await chatContext.get('firstName');
50
- assert.equal(firstName, 'Guidone');
51
- await chatContext.remove('firstName');
52
- firstName = await chatContext.get('firstName');
53
- assert.isUndefined(firstName);
54
- });
55
-
56
-
57
- it('should set some values and then get and remove it', async () => {
58
- const chatContext = await provider.getOrCreate(42, null, {});
59
- await chatContext.set({firstName: 'Guido', lastName: 'Bellomo'});
60
- let firstName = await chatContext.get('firstName');
61
- assert.equal(firstName, 'Guido');
62
- let lastName = await chatContext.get('lastName');
63
- assert.equal(lastName, 'Bellomo');
64
- let json = await chatContext.get('firstName', 'lastName');
65
- assert.isObject(json);
66
- assert.equal(json.firstName, 'Guido');
67
- assert.equal(json.lastName, 'Bellomo');
68
- });
69
-
70
- it('should set some values and then get and remove it with userId', async () => {
71
- const chatContext = await provider.getOrCreate(null, 43, {});
72
- await chatContext.set({ firstName: 'Guido', lastName: 'Bellomo' });
73
- let firstName = await chatContext.get('firstName');
74
- assert.equal(firstName, 'Guido');
75
- let lastName = await chatContext.get('lastName');
76
- assert.equal(lastName, 'Bellomo');
77
- let json = await chatContext.get('firstName', 'lastName');
78
- assert.isObject(json);
79
- assert.equal(json.firstName, 'Guido');
80
- assert.equal(json.lastName, 'Bellomo');
81
- });
82
-
83
- it('should set some values and get the dump with chatId', async () => {
84
- const chatContext = await provider.getOrCreate(42, null, {});
85
- await chatContext.set({firstName: 'Guido', lastName: 'Bellomo', email: 'spam@gmail.com'});
86
- const json = await chatContext.all();
87
- assert.isObject(json);
88
- assert.equal(json.firstName, 'Guido');
89
- assert.equal(json.lastName, 'Bellomo');
90
- assert.equal(json.email, 'spam@gmail.com');
91
- });
92
-
93
- it('should set some values and get the dump with userId', async () => {
94
- const chatContext = await provider.getOrCreate(null, 43, {});
95
- await chatContext.set({firstName: 'Guido', lastName: 'Bellomo', email: 'spam@gmail.com'});
96
- const json = await chatContext.all();
97
- assert.isObject(json);
98
- assert.equal(json.firstName, 'Guido');
99
- assert.equal(json.lastName, 'Bellomo');
100
- assert.equal(json.email, 'spam@gmail.com');
101
- });
102
-
103
- it('should set some values and remove all for chatId', async () => {
104
- const chatContext = await provider.getOrCreate(42, {});
105
- await chatContext.set({firstName: 'Guido', lastName: 'Bellomo'});
106
- await chatContext.clear();
107
- let firstName = await chatContext.get('firstName');
108
- assert.isUndefined(firstName);
109
- let lastName = await chatContext.get('lastName');
110
- assert.isUndefined(lastName);
111
- const json = await chatContext.all();
112
- assert.isObject(json);
113
- assert.isTrue(_.isEmpty(json));
114
- });
115
-
116
- it('should set some values and remove all for userId', async () => {
117
- const chatContext = await provider.getOrCreate(null, 43, {});
118
- await chatContext.set({firstName: 'Guido', lastName: 'Bellomo'});
119
- await chatContext.clear();
120
- const firstName = await chatContext.get('firstName');
121
- assert.isUndefined(firstName)
122
- const lastName = await chatContext.get('lastName');
123
- assert.isUndefined(lastName)
124
- const json = await chatContext.all();
125
- assert.isObject(json);
126
- assert.isTrue(_.isEmpty(json));
127
- });
128
-
129
- it('should set some value the remove with multiple arguments for chatId', async () => {
130
- const chatContext = await provider.getOrCreate(42, {});
131
- await chatContext.set({firstName: 'Guidone', lastName: 'Bellomo', email: 'some@email'})
132
- const firstName = await chatContext.get('firstName');
133
- assert.equal(firstName, 'Guidone');
134
- await chatContext.remove('firstName', 'lastName', 'email');
135
- const json = await chatContext.all();
136
- assert.isUndefined(json.firstName);
137
- assert.isUndefined(json.lastName);
138
- assert.isUndefined(json.email);
139
- });
140
-
141
- it('should set some value the remove with multiple arguments for userId', async () => {
142
- const chatContext = await provider.getOrCreate(null, 43, {});
143
- await chatContext.set({firstName: 'Guidone', lastName: 'Bellomo', email: 'some@email'});
144
- const firstName = await chatContext.get('firstName');
145
- assert.equal(firstName, 'Guidone');
146
- await chatContext.remove('firstName', 'lastName', 'email');
147
- const json = await chatContext.all();
148
- assert.isUndefined(json.firstName);
149
- assert.isUndefined(json.lastName);
150
- assert.isUndefined(json.email);
151
- });
152
-
153
- it('should set some value with chatId and userId', async () => {
154
- const chatContext = await provider.getOrCreate(42, 44, { userId: 44, chatId: 42 });
155
- await chatContext.set('firstName', 'Guidone');
156
- let firstName = await chatContext.get('firstName');
157
- assert.equal(firstName, 'Guidone');
158
- let userId = await chatContext.get('userId');
159
- assert.equal(userId, 44);
160
- let chatId = await chatContext.get('chatId');
161
- assert.equal(chatId, 42);
162
- const chatContext2 = await provider.getOrCreate(null, 44, { userId: 44, chatId: 42 });
163
- firstName = await chatContext2.get('firstName');
164
- assert.equal(firstName, 'Guidone');
165
- userId = await chatContext2.get('userId');
166
- assert.equal(userId, 44);
167
- chatId = await chatContext2.get('chatId');
168
- assert.equal(chatId, 42);
169
- await chatContext2.remove('firstName');
170
- firstName = await chatContext2.get('firstName');
171
- assert.isUndefined(firstName);
172
- });
173
-
174
- });