@hubot-friends/hubot-slack 0.0.0-development

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.
Files changed (72) hide show
  1. package/.github/CODE_OF_CONDUCT.md +11 -0
  2. package/.github/contributing.md +60 -0
  3. package/.github/issue_template.md +48 -0
  4. package/.github/maintainers_guide.md +91 -0
  5. package/.github/pull_request_template.md +8 -0
  6. package/.github/workflows/ci-build.yml +74 -0
  7. package/LICENSE +22 -0
  8. package/README.md +29 -0
  9. package/docs/Gemfile +2 -0
  10. package/docs/README.md +15 -0
  11. package/docs/_config.yml +29 -0
  12. package/docs/_includes/analytics.html +7 -0
  13. package/docs/_includes/head.html +33 -0
  14. package/docs/_includes/page_header.html +20 -0
  15. package/docs/_includes/side_nav.html +22 -0
  16. package/docs/_includes/tag_manager.html +13 -0
  17. package/docs/_layouts/changelog.html +15 -0
  18. package/docs/_layouts/default.html +47 -0
  19. package/docs/_layouts/page.html +10 -0
  20. package/docs/_pages/FAQ.md +63 -0
  21. package/docs/_pages/about.md +16 -0
  22. package/docs/_pages/advanced_usage.md +102 -0
  23. package/docs/_pages/auth.md +44 -0
  24. package/docs/_pages/basic_usage.md +302 -0
  25. package/docs/_pages/changelog.html +17 -0
  26. package/docs/_pages/upgrading.md +49 -0
  27. package/docs/_posts/2016-07-15-v4.0.0.md +14 -0
  28. package/docs/_posts/2016-07-19-v4.0.1.md +5 -0
  29. package/docs/_posts/2016-08-03-v4.0.2.md +4 -0
  30. package/docs/_posts/2016-09-12-v4.0.3.md +8 -0
  31. package/docs/_posts/2016-09-12-v4.0.4.md +4 -0
  32. package/docs/_posts/2016-09-14-v4.0.5.md +4 -0
  33. package/docs/_posts/2016-09-21-v4.1.0.md +4 -0
  34. package/docs/_posts/2016-10-12-v4.2.0.md +4 -0
  35. package/docs/_posts/2016-10-12-v4.2.1.md +4 -0
  36. package/docs/_posts/2016-11-05-v4.2.2.md +8 -0
  37. package/docs/_posts/2017-01-05-v4.3.0.md +5 -0
  38. package/docs/_posts/2017-01-09-v4.3.1.md +5 -0
  39. package/docs/_posts/2017-02-15-v4.3.2.md +5 -0
  40. package/docs/_posts/2017-02-17-v4.3.3.md +4 -0
  41. package/docs/_posts/2017-03-29-v4.3.4.md +5 -0
  42. package/docs/_posts/2017-08-24-v4.4.0.md +7 -0
  43. package/docs/_posts/2018-06-08-v4.5.0.md +13 -0
  44. package/docs/_posts/2018-06-14-v4.5.1.md +4 -0
  45. package/docs/_posts/2018-07-03-v4.5.2.md +5 -0
  46. package/docs/_posts/2018-07-17-v4.5.3.md +12 -0
  47. package/docs/_posts/2018-08-10-v4.5.4.md +7 -0
  48. package/docs/_posts/2018-10-01-v4.5.5.md +7 -0
  49. package/docs/_posts/2018-12-21-v4.6.0.md +6 -0
  50. package/docs/_posts/2019-04-29-v4.7.0.md +6 -0
  51. package/docs/_posts/2019-04-30-v4.7.1.md +5 -0
  52. package/docs/_posts/2020-04-03-v4.7.2.md +6 -0
  53. package/docs/_posts/2020-05-19-v4.8.0.md +10 -0
  54. package/docs/_posts/2020-10-19-v4.8.1.md +7 -0
  55. package/docs/_posts/2021-01-26-v4.9.0.md +8 -0
  56. package/docs/_posts/2022-01-12-v4.10.0.md +8 -0
  57. package/docs/index.md +93 -0
  58. package/docs/styles/docs.css +37 -0
  59. package/package.json +50 -0
  60. package/slack.js +20 -0
  61. package/src/SlackAdapter.mjs +302 -0
  62. package/src/SlackAdapter.test.mjs +342 -0
  63. package/src/bot.js +526 -0
  64. package/src/client.js +445 -0
  65. package/src/extensions.js +82 -0
  66. package/src/mention.js +15 -0
  67. package/src/message.js +307 -0
  68. package/src/testing.mjs +24 -0
  69. package/test/bot.js +769 -0
  70. package/test/client.js +446 -0
  71. package/test/message.js +329 -0
  72. package/test/stubs.js +388 -0
package/test/client.js ADDED
@@ -0,0 +1,446 @@
1
+
2
+ const { describe, it, beforeEach } = require('node:test');
3
+ const assert = require('node:assert/strict');
4
+ const Module = require('module');
5
+
6
+ const hookModuleToReturnMockFromRequire = (module, mock) => {
7
+ const originalRequire = Module.prototype.require;
8
+ Module.prototype.require = function() {
9
+ if (arguments[0] === module) {
10
+ return mock;
11
+ }
12
+ return originalRequire.apply(this, arguments);
13
+ };
14
+ };
15
+
16
+ const hubotSlackMock = require('../slack.js');
17
+ hookModuleToReturnMockFromRequire('hubot-slack', hubotSlackMock);
18
+
19
+ const SocketModeClient = require('@slack/socket-mode').SocketModeClient;
20
+ const WebClient = require('@slack/web-api').WebClient;
21
+
22
+
23
+ describe('Init', function() {
24
+ let stubs, slackbox, client;
25
+ beforeEach(function() {
26
+ ({ stubs, slackbot, client } = require('./stubs.js')());
27
+ });
28
+ it('Should initialize with an SocketModeClient client', function() {
29
+ assert.ok(client.socket instanceof SocketModeClient)
30
+ });
31
+
32
+ it('Should initialize with a Web client', function() {
33
+ assert.ok(client.web instanceof WebClient);
34
+ assert.deepEqual(client.web.token, 'xoxb-faketoken');
35
+ });
36
+
37
+ });
38
+
39
+ describe('connect()', () => {
40
+ let stubs, slackbox, client;
41
+ beforeEach(function() {
42
+ ({ stubs, slackbot, client } = require('./stubs.js')());
43
+ });
44
+ it('Should be able to connect', async () => {
45
+ await client.connect();
46
+ assert.ok(client.socket.connected);
47
+ });
48
+ });
49
+
50
+ describe('onEvent()', function() {
51
+ let stubs, slackbox, client;
52
+ beforeEach(function() {
53
+ ({ stubs, slackbot, client } = require('./stubs.js')());
54
+ });
55
+ it('should not need to be set', function() {
56
+ client.socket.emit('message', { fake: 'message' });
57
+ assert.ok(true);
58
+ });
59
+ it('should emit pre-processed messages to the callback', function(t, done) {
60
+ client.onEvent(message => {
61
+ assert.ok(message);
62
+ assert.deepEqual(message.user?.real_name, stubs.user.real_name);
63
+ assert.deepEqual(message.channel, stubs.channel.id);
64
+ done();
65
+ });
66
+ // the shape of the following object is a raw message event: https://api.slack.com/events/message
67
+ client.socket.emit('message', {
68
+ type: 'message',
69
+ text: 'blah',
70
+ user: stubs.user.id,
71
+ channel: stubs.channel.id,
72
+ ts: stubs.event_timestamp
73
+ });
74
+ assert.deepEqual(stubs.robot.logger.logs.error, undefined);
75
+ });
76
+ it('should successfully convert bot users', function(t, done) {
77
+ client.onEvent(message => {
78
+ assert.ok(message);
79
+ assert.deepEqual(message.user.id, stubs.user.id);
80
+ assert.deepEqual(message.channel, stubs.channel.id);
81
+ done();
82
+ });
83
+ // the shape of the following object is a raw message event: https://api.slack.com/events/message
84
+ client.socket.emit('message', {
85
+ type: 'message',
86
+ bot_id: 'B123',
87
+ channel: stubs.channel.id,
88
+ text: 'blah'
89
+ });
90
+ assert.deepEqual(stubs.robot.logger.logs.error, undefined);
91
+ });
92
+
93
+ it('should handle undefined bot users', function(t, done) {
94
+ client.onEvent(message => {
95
+ assert.ok(message);
96
+ assert.deepEqual(message.channel, stubs.channel.id);
97
+ done();
98
+ });
99
+ client.socket.emit('message', {
100
+ type: 'message',
101
+ bot_id: 'B789',
102
+ channel: stubs.channel.id,
103
+ text: 'blah'
104
+ });
105
+ assert.deepEqual(stubs.robot.logger.logs.error, undefined);
106
+ });
107
+
108
+ it('should handle undefined users as envisioned', function(t, done) {
109
+ client.onEvent(message => {
110
+ assert.ok(message);
111
+ assert.deepEqual(message.channel, stubs.channel.id);
112
+ done();
113
+ });
114
+ client.socket.emit('message', {
115
+ type: 'message',
116
+ user: undefined,
117
+ channel: stubs.channel.id,
118
+ text: 'eat more veggies'
119
+ });
120
+ assert.deepEqual(stubs.robot.logger.logs.error, undefined);
121
+ });
122
+
123
+ it('should update bot id to user representation map', function(t, done) {
124
+ client.onEvent(message => {
125
+ assert.ok(message);
126
+ assert.deepEqual(client.botUserIdMap[stubs.bot.id].id, stubs.user.id);
127
+ done();
128
+ });
129
+
130
+ // the shape of the following object is a raw message event: https://api.slack.com/events/message
131
+ client.socket.emit('message', {
132
+ type: 'message',
133
+ bot_id: stubs.bot.id,
134
+ channel: stubs.channel.id,
135
+ text: 'blah'
136
+ });
137
+ assert.deepEqual(stubs.robot.logger.logs.error, undefined);
138
+ });
139
+ it('should use user representation for bot id in map', function(t, done) {
140
+ client.onEvent(message => {
141
+ assert.ok(message);
142
+ assert.deepEqual(message.user.id, stubs.user.id);
143
+ done();
144
+ });
145
+
146
+ client.botUserIdMap[stubs.bot.id] = stubs.user;
147
+ // the shape of the following object is a raw message event: https://api.slack.com/events/message
148
+ client.socket.emit('message', {
149
+ type: 'message',
150
+ bot_id: stubs.bot.id,
151
+ channel: stubs.channel.id,
152
+ text: 'blah'
153
+ });
154
+ assert.deepEqual(stubs.robot.logger.logs.error, undefined);
155
+ });
156
+ it('should log an error when expanded info cannot be fetched using the Web API', function(t, done) {
157
+ client.onEvent(message => {
158
+ console.log('throwing error', message)
159
+ throw new Error('A message was emitted');
160
+ });
161
+ client.socket.emit('message', {
162
+ type: 'message',
163
+ user: 'NOT A USER',
164
+ channel: stubs.channel.id,
165
+ text: 'blah',
166
+ ts: '1355517523.000005'
167
+ });
168
+ // wait for the next tick to check the logs so that the error can be thrown
169
+ // and caught by the test framework. process.nextTick executes the function
170
+ // after everything in the current call stack has been executed.
171
+ process.nextTick(() => {
172
+ if (stubs.robot.logger.logs) {
173
+ console.log(JSON.stringify(stubs.robot.logger.logs))
174
+ assert.deepEqual(stubs.robot.logger.logs?.error?.length, 1);
175
+ }
176
+ done();
177
+ });
178
+ });
179
+
180
+ it('should use user instead of bot_id', function(t, done) {
181
+ client.onEvent(message => {
182
+ assert.ok(message);
183
+ assert.deepEqual(message.user.id, stubs.user.id);
184
+ done();
185
+ });
186
+
187
+ client.botUserIdMap[stubs.bot.id] = stubs.userperiod;
188
+ client.socket.emit('message', {
189
+ type: 'message',
190
+ bot_id: stubs.bot.id,
191
+ user: stubs.user.id,
192
+ channel: stubs.channel.id,
193
+ text: 'blah'
194
+ });
195
+ assert.deepEqual(stubs.robot.logger.logs.error, undefined);
196
+ });
197
+ });
198
+
199
+ describe('setTopic()', function() {
200
+ let stubs, slackbox, client;
201
+ beforeEach(function() {
202
+ ({ stubs, slackbot, client } = require('./stubs.js')());
203
+ });
204
+ it("Should set the topic in a channel", async function() {
205
+ await client.setTopic(stubs.channel.id, 'iAmTopic');
206
+ assert.deepEqual(stubs._topic, 'iAmTopic');
207
+ });
208
+ it("should not set the topic in a DM", async function() {
209
+ await client.setTopic(stubs.DM.id, 'iAmTopic');
210
+ assert.deepEqual(stubs['_topic'], undefined);
211
+ });
212
+ it("should not set the topic in a MPIM", async function() {
213
+ await client.setTopic(stubs.group.id, 'iAmTopic');
214
+ assert.deepEqual(stubs['_topic'], undefined);
215
+ // NOTE: no good way to assert that debug log was output
216
+ });
217
+ it("should log an error if the setTopic web API method fails", async function() {
218
+ await client.setTopic('NOT A CONVERSATION', 'iAmTopic');
219
+ assert.deepEqual(stubs['_topic'], undefined);
220
+ if (stubs.robot.logger.logs != null) {
221
+ assert.deepEqual(stubs.robot.logger.logs.error.length, 1);
222
+ }
223
+ });
224
+ });
225
+
226
+ describe('send()', function() {
227
+ let stubs, slackbox, client;
228
+ beforeEach(function() {
229
+ ({ stubs, slackbot, client } = require('./stubs.js')());
230
+ });
231
+ it('Should send a plain string message to room', function() {
232
+ client.send({room: 'room1'}, {text: 'Message'});
233
+ assert.deepEqual(stubs._msg, 'Message');
234
+ assert.deepEqual(stubs._room, 'room1');
235
+ });
236
+
237
+ it('Should send an object message to room', function() {
238
+ client.send({room: 'room2'}, {text: 'textMessage'});
239
+ assert.deepEqual(stubs._msg, 'textMessage');
240
+ assert.deepEqual(stubs._room, 'room2');
241
+ });
242
+
243
+ it('Should be able to send a DM to a user object', function() {
244
+ client.send({ room: stubs.user.id }, {text: 'DM Message'});
245
+ assert.deepEqual(stubs._dmmsg, 'DM Message');
246
+ assert.deepEqual(stubs._room, stubs.user.id);
247
+ });
248
+
249
+ it('should not send a message to a user without an ID', function() {
250
+ client.send({ name: 'my_crufty_username'}, {text: "don't program with usernames"});
251
+ assert.deepEqual(stubs._sendCount, 0);
252
+ });
253
+
254
+ it('should log an error when chat.postMessage fails (plain string)', function(t, done) {
255
+ client.send({ room: stubs.channelWillFailChatPost}, {text: "Message"});
256
+ assert.deepEqual(stubs._sendCount, 0);
257
+ return setImmediate(( () => {
258
+ if (stubs.robot.logger.logs != null) {
259
+ assert.deepEqual(stubs.robot.logger.logs.error.length, 1);
260
+ }
261
+ done();
262
+ }
263
+ ), 0);
264
+ });
265
+
266
+ it('should log an error when chat.postMessage fails (object)', function(t, done) {
267
+ client.send({ room: stubs.channelWillFailChatPost}, {text: "textMessage"});
268
+ assert.deepEqual(stubs._sendCount, 0);
269
+ return setImmediate(( () => {
270
+ if (stubs.robot.logger.logs != null) {
271
+ assert.deepEqual(stubs.robot.logger.logs.error.length, 1);
272
+ }
273
+ done();
274
+ }
275
+ ), 0);
276
+ });
277
+ });
278
+
279
+ describe('loadUsers()', function() {
280
+ let stubs, slackbox, client;
281
+ beforeEach(function() {
282
+ ({ stubs, slackbot, client } = require('./stubs.js')());
283
+ });
284
+ it('should make successive calls to users.list', function() {
285
+ return client.loadUsers((err, result) => {
286
+ if (stubs != null) {
287
+ assert.deepEqual(stubs._listCount, 2);
288
+ }
289
+ assert.deepEqual(result.members.length, 4);
290
+ });
291
+ });
292
+ it('should handle errors', function() {
293
+ stubs._listError = true;
294
+ return client.loadUsers((err, result) => {
295
+ assert.ok(err instanceof Error);
296
+ });
297
+ });
298
+ });
299
+
300
+ describe('Users data', function() {
301
+ let stubs, slackbox, client;
302
+ beforeEach(function() {
303
+ ({ stubs, slackbot, client } = require('./stubs.js')());
304
+ });
305
+ it('Should add a user data', function() {
306
+ client.updateUserInBrain(stubs.user);
307
+ const user = slackbot.robot.brain.data.users[stubs.user.id];
308
+ assert.deepEqual(user.id, stubs.user.id);
309
+ assert.deepEqual(user.name, stubs.user.name);
310
+ assert.deepEqual(user.real_name, stubs.user.real_name);
311
+ assert.deepEqual(user.email_address, stubs.user.profile.email);
312
+ assert.deepEqual(user.slack.misc, stubs.user.misc);
313
+ });
314
+
315
+ it('Should add a user data (user with no profile)', function() {
316
+ client.updateUserInBrain(stubs.usernoprofile);
317
+ const user = slackbot.robot.brain.data.users[stubs.usernoprofile.id];
318
+ assert.deepEqual(user.id, stubs.usernoprofile.id);
319
+ assert.deepEqual(user.name, stubs.usernoprofile.name);
320
+ assert.deepEqual(user.real_name, stubs.usernoprofile.real_name);
321
+ assert.deepEqual(user.slack.misc, stubs.usernoprofile.misc);
322
+ assert.ok(user.email_address == undefined);
323
+ });
324
+
325
+ it('Should add a user data (user with no email in profile)', function() {
326
+ client.updateUserInBrain(stubs.usernoemail);
327
+
328
+ const user = slackbot.robot.brain.data.users[stubs.usernoemail.id];
329
+ assert.deepEqual(user.id, stubs.usernoemail.id);
330
+ assert.deepEqual(user.name, stubs.usernoemail.name);
331
+ assert.deepEqual(user.real_name, stubs.usernoemail.real_name);
332
+ assert.deepEqual(user.slack.misc, stubs.usernoemail.misc);
333
+ assert.ok(user.email_address == undefined);
334
+ });
335
+
336
+ it('Should modify a user data', function() {
337
+ client.updateUserInBrain(stubs.user);
338
+
339
+ let user = slackbot.robot.brain.data.users[stubs.user.id];
340
+ assert.deepEqual(user.id, stubs.user.id);
341
+ assert.deepEqual(user.name, stubs.user.name);
342
+ assert.deepEqual(user.real_name, stubs.user.real_name);
343
+ assert.deepEqual(user.email_address, stubs.user.profile.email);
344
+ assert.deepEqual(user.slack.misc, stubs.user.misc);
345
+
346
+ const user_change_event = {
347
+ type: 'user_change',
348
+ user: {
349
+ id: stubs.user.id,
350
+ name: 'modified_name',
351
+ real_name: stubs.user.real_name,
352
+ profile: {
353
+ email: stubs.user.profile.email
354
+ }
355
+ }
356
+ };
357
+
358
+ client.updateUserInBrain(user_change_event);
359
+
360
+ user = slackbot.robot.brain.data.users[stubs.user.id];
361
+ assert.deepEqual(user.id, stubs.user.id);
362
+ assert.deepEqual(user.name, user_change_event.user.name);
363
+ assert.deepEqual(user.real_name, stubs.user.real_name);
364
+ assert.deepEqual(user.email_address, stubs.user.profile.email);
365
+ assert.deepEqual(user.slack.misc, undefined);
366
+ assert.deepEqual(user.slack.client, undefined);
367
+ });
368
+ });
369
+
370
+ describe('fetchBotUser()', function() {
371
+ let stubs, slackbox, client;
372
+ beforeEach(function() {
373
+ ({ stubs, slackbot, client } = require('./stubs.js')());
374
+ });
375
+ it('should return user representation from map', async function() {
376
+ const {
377
+ user
378
+ } = stubs;
379
+ client.botUserIdMap[stubs.bot.id] = user;
380
+ const res = await client.fetchBotUser(stubs.bot.id)
381
+ assert.deepEqual(res.id, user.id);
382
+ });
383
+
384
+ it('should return constant data if id is slackbots id', async function() {
385
+ const user = stubs.slack_bot;
386
+ const res = await client.fetchBotUser(stubs.slack_bot.id)
387
+ assert.deepEqual(res.id, user.id);
388
+ assert.deepEqual(res.user_id, user.user_id);
389
+ });
390
+ });
391
+
392
+ describe('fetchUser()', function() {
393
+ let stubs, slackbox, client;
394
+ beforeEach(function() {
395
+ ({ stubs, slackbot, client } = require('./stubs.js')());
396
+ });
397
+ it('should return user representation from brain', async function() {
398
+ const {
399
+ user
400
+ } = stubs;
401
+ client.updateUserInBrain(user);
402
+ const res = await client.fetchUser(user.id)
403
+ assert.deepEqual(res.id, user.id);
404
+ });
405
+
406
+ it('Should sync interacting users when syncing disabled', async function() {
407
+ slackbot.options.disableUserSync = true;
408
+ slackbot.run();
409
+
410
+ const res = await client.fetchUser(stubs.user.id)
411
+ assert.ok(Object.keys(slackbot.robot.brain.data.users).includes('U123'));
412
+ });
413
+ });
414
+
415
+ describe('fetchConversation()', function() {
416
+ let stubs, slackbox, client;
417
+ beforeEach(function() {
418
+ ({ stubs, slackbot, client } = require('./stubs.js')());
419
+ });
420
+ it('Should remove expired conversation info', async function() {
421
+ const {
422
+ channel
423
+ } = stubs;
424
+ client.channelData[channel.id] = {
425
+ channel: {id: 'C123', name: 'foo'},
426
+ updated: stubs.expired_timestamp
427
+ };
428
+ const res = await client.fetchConversation(channel.id)
429
+ assert.deepEqual(res.name, channel.name);
430
+ assert.ok(Object.keys(client.channelData).includes('C123'));
431
+ assert.deepEqual(client.channelData['C123'].channel.name, channel.name);
432
+ });
433
+ it('Should return conversation info if not expired', async function() {
434
+ const {
435
+ channel
436
+ } = stubs;
437
+ client.channelData[channel.id] = {
438
+ channel: {id: 'C123', name: 'foo'},
439
+ updated: Date.now()
440
+ };
441
+ const res = await client.fetchConversation(channel.id)
442
+ assert.deepEqual(res.id, channel.id);
443
+ assert.ok(Object.keys(client.channelData).includes('C123'));
444
+ assert.deepEqual(client.channelData['C123'].channel.name, 'foo');
445
+ });
446
+ });