chat-platform 1.2.3

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.
@@ -0,0 +1,239 @@
1
+ const _ = require('underscore');
2
+ const assert = require('chai').assert;
3
+ const fs = require('fs');
4
+ const RED = require('../lib/red-stub')();
5
+ const ContextProviders = require('../chat-context-factory');
6
+ const { when } = require('../lib/utils');
7
+ const os = require('os');
8
+
9
+ const copyFile = (source, destination) => new Promise((resolve, reject) => {
10
+ fs.copyFile(source, destination, error => {
11
+ if (error != null) {
12
+ reject(error);
13
+ } else {
14
+ resolve();
15
+ }
16
+ });
17
+ });
18
+
19
+ const removeFile = file => new Promise((resolve, reject) => {
20
+ fs.unlink(file, error => {
21
+ if (error != null) {
22
+ reject(error);
23
+ } else {
24
+ resolve();
25
+ }
26
+ });
27
+ });
28
+
29
+ describe('Chat context provider sqlite - missing file', () => {
30
+ const contextProviders = ContextProviders(RED);
31
+ const path = os.tmpdir();
32
+ const getProvider = () => contextProviders.getProvider('sqlite', { dbPath: path + '/mission-control.sqlite' });
33
+
34
+ beforeAll(async () => {
35
+ const provider = getProvider();
36
+ await provider.start();
37
+ });
38
+
39
+ afterAll(async () => {
40
+ await removeFile(path + '/mission-control.sqlite');
41
+ });
42
+
43
+ it('should create a context provider with some default params with chatId', async () => {
44
+
45
+ assert.isTrue(contextProviders.hasProvider('sqlite'));
46
+ const provider = getProvider();
47
+ assert.isFunction(provider.getOrCreate);
48
+ assert.isFunction(provider.get);
49
+ const chatContext = await provider.getOrCreate(43, null, { chatId: 43 });
50
+ await chatContext.set({ myVariable: 'initial value' })
51
+ assert.isFunction(chatContext.get);
52
+ assert.isFunction(chatContext.set);
53
+ assert.isFunction(chatContext.all);
54
+ assert.isFunction(chatContext.remove);
55
+ assert.isFunction(chatContext.clear);
56
+ const myVariable = await chatContext.get('myVariable');
57
+
58
+ assert.equal(myVariable, 'initial value');
59
+ });
60
+
61
+
62
+ });
63
+
64
+
65
+ describe('Chat context provider sqlite', () => {
66
+ const contextProviders = ContextProviders(RED);
67
+ const getProvider = () => contextProviders.getProvider('sqlite', { dbPath: __dirname + '/dummy/mission-control.sqlite' });
68
+
69
+ beforeAll(async () => {
70
+ await copyFile(__dirname + '/dummy/mission-control.backup', __dirname + '/dummy/mission-control.sqlite');
71
+ const provider = getProvider();
72
+ await provider.start();
73
+ });
74
+
75
+ afterAll(async () => {
76
+ await removeFile(__dirname + '/dummy/mission-control.sqlite');
77
+ });
78
+
79
+ beforeEach(async () => {
80
+ const provider = getProvider();
81
+ await provider.reset();
82
+ });
83
+
84
+ it('should create a context provider with some default params with chatId', async () => {
85
+
86
+ assert.isTrue(contextProviders.hasProvider('sqlite'));
87
+ const provider = getProvider();
88
+ assert.isFunction(provider.getOrCreate);
89
+ assert.isFunction(provider.get);
90
+ const chatContext = await provider.getOrCreate(43, null, { chatId: 43 });
91
+ await chatContext.set({ myVariable: 'initial value' })
92
+ assert.isFunction(chatContext.get);
93
+ assert.isFunction(chatContext.set);
94
+ assert.isFunction(chatContext.all);
95
+ assert.isFunction(chatContext.remove);
96
+ assert.isFunction(chatContext.clear);
97
+ const myVariable = await chatContext.get('myVariable');
98
+
99
+ assert.equal(myVariable, 'initial value');
100
+ });
101
+
102
+ it('should create a context provider with some default params with userId', async () => {
103
+ assert.isTrue(contextProviders.hasProvider('sqlite'));
104
+ const provider = getProvider();
105
+ assert.isFunction(provider.getOrCreate);
106
+ assert.isFunction(provider.get);
107
+ const chatContext = await provider.getOrCreate(null, 44, { userId: 44 });
108
+ await chatContext.set({ myVariable: 'initial value' })
109
+ assert.isFunction(chatContext.get);
110
+ assert.isFunction(chatContext.set);
111
+ assert.isFunction(chatContext.all);
112
+ assert.isFunction(chatContext.remove);
113
+ assert.isFunction(chatContext.clear);
114
+ const myVariable = await chatContext.get('myVariable');
115
+ assert.equal(myVariable, 'initial value');
116
+ });
117
+
118
+
119
+ it('should set some value and then get and remove it with chatId', async () => {
120
+ const provider = getProvider();
121
+ const chatContext = await provider.getOrCreate(42, null, { chatId: 42 });
122
+ await chatContext.set('firstName', 'Guidone');
123
+ let firstName = await chatContext.get('firstName');
124
+ assert.equal(firstName, 'Guidone')
125
+ await chatContext.remove('firstName')
126
+ firstName = await chatContext.get('firstName')
127
+ assert.isNull(firstName);
128
+ });
129
+
130
+ it('should set some value and then get and remove it with userId', async () => {
131
+ const provider = getProvider();
132
+ const chatContext = await provider.getOrCreate(null, 43, { userId: 43 });
133
+ await chatContext.set('firstName', 'Guidone');
134
+ let firstName = await chatContext.get('firstName');
135
+ assert.equal(firstName, 'Guidone');
136
+ await chatContext.remove('firstName')
137
+ firstName = await chatContext.get('firstName');
138
+ assert.isNull(firstName);
139
+ });
140
+
141
+
142
+ it('should set some values and then get and remove it with chatId', async () => {
143
+ const provider = getProvider();
144
+ const chatContext = await provider.getOrCreate(42, null, { chatId: 42 });
145
+ await chatContext.set({ firstName: 'Guido', lastName: 'Bellomo' });
146
+ let firstName = await chatContext.get('firstName');
147
+ assert.equal(firstName, 'Guido');
148
+ let lastName = await chatContext.get('lastName');
149
+ assert.equal(lastName, 'Bellomo')
150
+ const json = await chatContext.get('firstName', 'lastName');
151
+ assert.isObject(json);
152
+ assert.equal(json.firstName, 'Guido');
153
+ assert.equal(json.lastName, 'Bellomo');
154
+ });
155
+
156
+ it('should set some values and then get and remove it with userId', async () => {
157
+ const provider = getProvider();
158
+ const chatContext = await provider.getOrCreate(null, 43, { userId: 43 });
159
+ await chatContext.set({firstName: 'Guido', lastName: 'Bellomo'});
160
+ const firstName = await chatContext.get('firstName');
161
+ assert.equal(firstName, 'Guido');
162
+ const lastName = await chatContext.get('lastName');
163
+ assert.equal(lastName, 'Bellomo');
164
+ const json = await chatContext.get('firstName', 'lastName');
165
+ assert.isObject(json);
166
+ assert.equal(json.firstName, 'Guido');
167
+ assert.equal(json.lastName, 'Bellomo');
168
+ });
169
+
170
+ it('should set some values and get the dump with chatId', async () => {
171
+ const provider = getProvider();
172
+ const chatContext = await provider.getOrCreate(42, null, { chatId: 42 });
173
+ await chatContext.set({ firstName: 'Guido', lastName: 'Bellomo', email: 'spam@gmail.com' });
174
+ const json = await chatContext.all();
175
+ assert.isObject(json);
176
+ assert.equal(json.firstName, 'Guido');
177
+ assert.equal(json.lastName, 'Bellomo');
178
+ assert.equal(json.email, 'spam@gmail.com');
179
+ });
180
+
181
+ it('should set some values and get the dump with userId', async () => {
182
+ const provider = getProvider();
183
+ const chatContext = await provider.getOrCreate(null, 43, { userId: 43 });
184
+ await chatContext.set({ firstName: 'Guido', lastName: 'Bellomo', email: 'spam@gmail.com' })
185
+ const json = await chatContext.all();
186
+ assert.isObject(json);
187
+ assert.equal(json.firstName, 'Guido');
188
+ assert.equal(json.lastName, 'Bellomo');
189
+ assert.equal(json.email, 'spam@gmail.com');
190
+ });
191
+
192
+ it('should set some values and remove all with chatId', async () => {
193
+ const provider = getProvider();
194
+ const chatContext = await provider.getOrCreate(42, null, { userId: 42 });
195
+ await chatContext.set({ firstName: 'Guido', lastName: 'Bellomo' });
196
+ await chatContext.clear();
197
+ const json = await chatContext.all();
198
+ assert.isObject(json);
199
+ assert.isTrue(_.isEmpty(json));
200
+ });
201
+
202
+ it('should set some values and remove all with userId', async () => {
203
+ const provider = getProvider();
204
+ const chatContext = await provider.getOrCreate(null, 43, { userId: 43 });
205
+ await chatContext.set({ firstName: 'Guido', lastName: 'Bellomo' });
206
+ await chatContext.clear();
207
+ const json = await chatContext.all();
208
+ assert.isObject(json);
209
+ assert.isTrue(_.isEmpty(json));
210
+ });
211
+
212
+ it('should set some value the remove with multiple arguments', async () => {
213
+ const provider = getProvider();
214
+ const chatContext = await provider.getOrCreate(42, null, { chatId: 42 });
215
+ await chatContext.set({ firstName: 'Guidone', lastName: 'Bellomo', email: 'some@email' });
216
+ const firstName = await chatContext.get('firstName');
217
+ assert.equal(firstName, 'Guidone');
218
+ await chatContext.remove('firstName', 'lastName', 'email');
219
+ const json = await chatContext.all();
220
+ assert.isUndefined(json.firstName);
221
+ assert.isUndefined(json.lastName);
222
+ assert.isUndefined(json.email);
223
+ });
224
+
225
+ it('should set some value with chatId and userId', async () => {
226
+ const provider = getProvider();
227
+ let chatContext = await provider.getOrCreate(42, 44, { userId: 44, chatId: 42 });
228
+ await chatContext.set('firstName', 'Guidone');
229
+ let firstName = await chatContext.get('firstName');
230
+ assert.equal(firstName, 'Guidone');
231
+ chatContext = await provider.getOrCreate(null, 44, { userId: 44, chatId: 42 });
232
+ assert.equal(await chatContext.get('userId'), 44);
233
+ assert.equal(await chatContext.get('firstName'), 'Guidone');
234
+ await chatContext.remove('firstName');
235
+ firstName = await chatContext.get('firstName');
236
+ assert.isNull(firstName);
237
+ });
238
+
239
+ });
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,190 @@
1
+ const moment = require('moment');
2
+ const chai = require('chai');
3
+ const assert = chai.assert;
4
+ const expect = chai.expect;
5
+ const spies = require('chai-spies');
6
+ const RED = require('../lib/red-stub')();
7
+ const Universal = require('../universal');
8
+ const ContextProviders = require('../chat-context-factory');
9
+ const contextProviders = ContextProviders(RED);
10
+
11
+ chai.use(spies);
12
+
13
+ describe('Universal Connector', () => {
14
+
15
+ let chatServer = null;
16
+ let chatServerAlt = null;
17
+ let sendFunction = null;
18
+
19
+ beforeEach(() => {
20
+ // Fake chat server
21
+ sendFunction = chai.spy(() => { });
22
+ chatServer = Universal.createServer({
23
+ contextProvider: contextProviders.getProvider('memory'),
24
+ debug: false
25
+ });
26
+ chatServer.onUserId(payload => payload.user.id);
27
+ chatServer.onChatId(payload => payload.chat_id);
28
+ chatServer.onMessageId(payload => payload._id);
29
+ chatServer.onLanguage(payload =>payload.lang);
30
+ chatServer.in(message => {
31
+ return new Promise((resolve) => {
32
+ const chat = message.chat();
33
+ if (message.originalMessage.text != null) {
34
+ chat.set('payload', message.originalMessage.text);
35
+ message.payload.content = message.originalMessage.text;
36
+ message.payload.type = 'message';
37
+ }
38
+ resolve(message);
39
+ });
40
+ });
41
+ chatServer.use(message => new Promise((resolve, reject) => {
42
+ message.custom_value = 42;
43
+ resolve(message);
44
+ })
45
+ );
46
+ chatServer.out('a-message-type', message => new Promise(resolve => {
47
+ message.message_id = '444';
48
+ sendFunction();
49
+ resolve(message);
50
+ })
51
+ );
52
+ // Alt fake server
53
+ sendFunctionAlt = chai.spy(() => { });
54
+ chatServerAlt = Universal.createServer({
55
+ contextProvider: contextProviders.getProvider('memory'),
56
+ transport: 'universal-alt',
57
+ debug: false
58
+ });
59
+ chatServerAlt.onUserId(payload => payload.user.id);
60
+ chatServerAlt.onChatId(payload => payload.chat_id);
61
+ chatServerAlt.onMessageId(payload => payload._id);
62
+ chatServerAlt.onLanguage(payload =>payload.lang);
63
+ chatServerAlt.in(message => {
64
+ return new Promise((resolve) => {
65
+ const chat = message.chat();
66
+ if (message.originalMessage.text != null) {
67
+ chat.set('payload', message.originalMessage.text);
68
+ message.payload.content = message.originalMessage.text;
69
+ message.payload.type = 'message';
70
+ }
71
+ resolve(message);
72
+ });
73
+ });
74
+ chatServerAlt.use(message => new Promise((resolve, reject) => {
75
+ message.custom_value = 42;
76
+ resolve(message);
77
+ })
78
+ );
79
+ chatServerAlt.out('a-message-type', message => new Promise(resolve => {
80
+ message.message_id = '445';
81
+ sendFunctionAlt();
82
+ resolve(message);
83
+ })
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';
91
+ }
92
+ return null;
93
+ });
94
+ });
95
+
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
+ });
122
+
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
+ });
136
+ });
137
+
138
+ it('send outgoing message wrong type', () => {
139
+ return chatServer.start()
140
+ .then(() => chatServer.createMessage('42', '1234', '4567', {
141
+ payload: {
142
+ type: 'a-different-type',
143
+ chatId: '42'
144
+ }
145
+ }))
146
+ .then(message => chatServer.send(message))
147
+ .then(() => expect(sendFunction).to.not.have.been.called());
148
+ });
149
+
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
+ });
Binary file
@@ -0,0 +1,92 @@
1
+ const _ = require('underscore');
2
+
3
+ const contextProviders = {};
4
+ contextProviders.memory = require('./providers/memory');
5
+ contextProviders.sqlite = require('./providers/sqlite');
6
+ contextProviders['plain-file'] = require('./providers/plain-file');
7
+
8
+ let _contexts = {};
9
+
10
+ const ContextProviders = function(RED) {
11
+
12
+ RED = RED || {};
13
+ // register Slack server
14
+ if (RED.redbot == null) {
15
+ RED.redbot = {};
16
+ }
17
+ if (RED.redbot.contextProviders == null) {
18
+ RED.redbot.contextProviders = {};
19
+ }
20
+
21
+ if (RED.settings != null) {
22
+ RED.settings.set('contextProviders', contextProviders);
23
+ }
24
+
25
+ if (RED.redbot.utils == null) {
26
+ RED.redbot.utils = {
27
+ when: function (param) {
28
+ if (param != null && _.isFunction(param.then)) {
29
+ return param;
30
+ } else if (param != null) {
31
+ return new Promise(function(resolve) {
32
+ resolve(param);
33
+ })
34
+ }
35
+ return new Promise(function(resolve, reject) {
36
+ reject();
37
+ });
38
+ }
39
+ };
40
+ }
41
+
42
+ const methods = {
43
+
44
+ when(param) {
45
+ if (param != null && _.isFunction(param.then)) {
46
+ return param;
47
+ } else if (param != null) {
48
+ return new Promise(function(resolve) {
49
+ resolve(param);
50
+ })
51
+ }
52
+ return new Promise(function(resolve, reject) {
53
+ reject();
54
+ });
55
+ },
56
+
57
+ getProviders() {
58
+ return _.keys(contextProviders);
59
+ },
60
+
61
+ hasProvider(provider) {
62
+ return _(methods.getProviders()).contains(provider);
63
+ },
64
+
65
+ getProvider(providerName, params = {}) {
66
+ const provider = contextProviders[providerName];
67
+ const context = new provider(params);
68
+ if (params.id != null) {
69
+ _contexts[params.id] = context;
70
+ }
71
+ return context;
72
+ },
73
+
74
+ reset() {
75
+ _contexts = {};
76
+ }
77
+ };
78
+
79
+ return methods;
80
+ };
81
+
82
+ // Static methods
83
+
84
+ ContextProviders.getProviderById = function(id) {
85
+ return _contexts[id];
86
+ };
87
+
88
+ ContextProviders.reset = function() {
89
+ return _contexts = {};
90
+ };
91
+
92
+ module.exports = ContextProviders;
package/chat-log.js ADDED
@@ -0,0 +1,115 @@
1
+ var _ = require('underscore');
2
+ var moment = require('moment');
3
+ var fs = require('fs');
4
+ var lcd = require('./helpers/lcd');
5
+
6
+ module.exports = function(variables) {
7
+
8
+ var self = {
9
+
10
+ log: function(msg, fileName) {
11
+ return new Promise(function(resolve) {
12
+ if (_.isEmpty(fileName)) {
13
+ // resolve immediately if empty and do nothing
14
+ resolve(msg);
15
+ } else {
16
+ fs.appendFile(fileName, self.message(msg) + '\n', function (err) {
17
+ if (err) {
18
+ // eslint-disable-next-line no-console
19
+ console.error(err);
20
+ }
21
+ // resolve anyway, problem on logging should not stop the chatbot
22
+ resolve(msg);
23
+ });
24
+ }
25
+ });
26
+ },
27
+
28
+ toString: function(msg) {
29
+
30
+ var logString = null;
31
+
32
+ if (_.isObject(msg.payload)) {
33
+
34
+ switch (msg.payload.type) {
35
+ case 'message':
36
+ logString = msg.payload.content;
37
+ break;
38
+ case 'location':
39
+ logString = 'latitude: ' + msg.payload.content.latitude + ' longitude: ' + msg.payload.content.latitude;
40
+ break;
41
+ case 'photo':
42
+ logString = 'image: ' + (msg.payload.filename != null ? msg.payload.filename : '<buffer>');
43
+ break;
44
+ case 'video':
45
+ logString = 'video: ' + (msg.payload.filename != null ? msg.payload.filename : '<buffer>');
46
+ break;
47
+ case 'document':
48
+ logString = 'document: ' + (msg.payload.filename != null ? msg.payload.filename : '<buffer>');
49
+ break;
50
+ case 'audio':
51
+ logString = 'audio: ' + (msg.payload.filename != null ? msg.payload.filename : '<buffer>');
52
+ break;
53
+ case 'inline-buttons':
54
+ logString = msg.payload.content + ' ' + msg.payload.buttons.map(function (button) {
55
+ return '[' + button.label + ']';
56
+ }).join(' ');
57
+ break;
58
+ case 'intent':
59
+ logString = 'intent: ' + msg.payload.intent + ' vars: ' + JSON.stringify(msg.payload.variables);
60
+ break;
61
+ case 'event':
62
+ logString = 'event: ' + msg.payload.eventType;
63
+ break;
64
+ case 'speech':
65
+ logString = 'speech: '
66
+ + (!_.isEmpty(msg.payload.text) ? msg.payload.text : '')
67
+ + (!_.isEmpty(msg.payload.ssml) ? msg.payload.ssml : '');
68
+ break;
69
+ case 'directive':
70
+ logString = 'directive: ' + msg.payload.directiveType;
71
+ break;
72
+ default:
73
+ // eslint-disable-next-line no-console
74
+ console.log(lcd.warn('Un-handled message type ' + msg.payload.type + ' in logs'));
75
+ logString = msg.payload.content instanceof Buffer ? '<buffer>' : String(msg.payload.content);
76
+ }
77
+ }
78
+
79
+ return logString;
80
+ },
81
+
82
+ message: function(msg) {
83
+
84
+ var chatId = variables.chatId || msg.originalMessage.chatId || msg.payload.chatId;
85
+ var inbound = msg.payload != null && msg.payload.inbound === true;
86
+ var firstName = variables.firstName;
87
+ var lastName = variables.lastName;
88
+ var transport = msg.originalMessage != null ? msg.originalMessage.transport : null;
89
+ var logString = null;
90
+
91
+ var name = [];
92
+ if (firstName != null) {
93
+ name.push(firstName);
94
+ }
95
+ if (lastName != null) {
96
+ name.push(lastName);
97
+ }
98
+
99
+ if (_.isObject(msg.payload)) {
100
+ var stringifiedMessage = this.toString(msg);
101
+ if (!_.isEmpty(stringifiedMessage)) {
102
+ logString = chatId + ' '
103
+ + (!_.isEmpty(name) ? '[' + name.join(' ') + '] ' : '')
104
+ + (inbound ? '> ' : '< ')
105
+ + (transport != null ? '[' + transport.toUpperCase() + '] ' : '')
106
+ + moment().toString() + ' - ' + stringifiedMessage;
107
+ }
108
+ }
109
+
110
+ return logString;
111
+ }
112
+ };
113
+
114
+ return self;
115
+ };