@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
@@ -0,0 +1,342 @@
1
+ import { describe, it, beforeEach, afterEach } from 'node:test'
2
+ import assert from 'node:assert/strict'
3
+ import { SlackAdapter } from './SlackAdapter.mjs'
4
+ import Robot from 'hubot/src/robot.js'
5
+ import { TextMessage } from 'hubot/src/message.js'
6
+ import EventEmitter from 'node:events'
7
+
8
+ const BOT_ID = 'U0AAATTTTTAAAAA'
9
+
10
+ class SlackClientMock extends EventEmitter {
11
+ #useRtmConnect = false
12
+ #delegate = null
13
+ constructor(token, useRtmConnect, delegate) {
14
+ super()
15
+ this.token = token
16
+ this.#useRtmConnect = useRtmConnect
17
+ this.#delegate = delegate
18
+ }
19
+ async start() {
20
+ if(this.#delegate?.start) {
21
+ return await this.#delegate.start(this)
22
+ }
23
+ this.emit('authenticated', new Error('Not authenticated'))
24
+ }
25
+ }
26
+ const buildATestUser = () => ({
27
+ ok: true,
28
+ user: {
29
+ id: 'U123ABC456',
30
+ team_id: 'TASDASDFSDD',
31
+ name: 'bossdog',
32
+ deleted: false,
33
+ color: 'e96699',
34
+ real_name: 'Joey Guerra',
35
+ tz: 'America/Chicago',
36
+ tz_label: 'Central Daylight Time',
37
+ tz_offset: -18000,
38
+ profile: {
39
+ title: 'type',
40
+ phone: '555555555',
41
+ skype: '',
42
+ real_name: 'Joey Guerra',
43
+ real_name_normalized: 'Joey Guerra',
44
+ display_name: 'joey',
45
+ display_name_normalized: 'joey',
46
+ fields: null,
47
+ status_text: '',
48
+ status_emoji: '',
49
+ status_emoji_display_info: [],
50
+ status_expiration: 0,
51
+ avatar_hash: 'a350644dd441',
52
+ image_original: 'https://avatars.slack-edge.com/2018-08-02/408988542176_a350644dd4414bddea09_original.jpg',
53
+ is_custom_image: true,
54
+ first_name: 'Joey',
55
+ last_name: 'Guerra',
56
+ image_24: 'https://avatars.slack-edge.com/2018-08-02/408988542176_a350644dd4414bddea09_24.jpg',
57
+ image_32: 'https://avatars.slack-edge.com/2018-08-02/408988542176_a350644dd4414bddea09_32.jpg',
58
+ image_48: 'https://avatars.slack-edge.com/2018-08-02/408988542176_a350644dd4414bddea09_48.jpg',
59
+ image_72: 'https://avatars.slack-edge.com/2018-08-02/408988542176_a350644dd4414bddea09_72.jpg',
60
+ image_192: 'https://avatars.slack-edge.com/2018-08-02/408988542176_a350644dd4414bddea09_192.jpg',
61
+ image_512: 'https://avatars.slack-edge.com/2018-08-02/408988542176_a350644dd4414bddea09_512.jpg',
62
+ image_1024: 'https://avatars.slack-edge.com/2018-08-02/408988542176_a350644dd4414bddea09_1024.jpg',
63
+ status_text_canonical: '',
64
+ team: 'TASDASDFSDD'
65
+ },
66
+ is_admin: true,
67
+ is_owner: false,
68
+ is_primary_owner: false,
69
+ is_restricted: false,
70
+ is_ultra_restricted: false,
71
+ is_bot: false,
72
+ is_app_user: false,
73
+ updated: 1629316975,
74
+ is_email_confirmed: true,
75
+ who_can_share_contact_card: 'EVERYONE'
76
+ },
77
+ response_metadata: {
78
+ scopes: [
79
+ 'app_mentions:read', 'channels:history',
80
+ 'channels:join', 'channels:read',
81
+ 'channels:write.topic', 'chat:write',
82
+ 'commands', 'files:read',
83
+ 'files:write', 'groups:history',
84
+ 'users:write', 'links:read',
85
+ 'im:history', 'im:read',
86
+ 'reactions:read', 'reactions:write',
87
+ 'groups:read', 'mpim:read',
88
+ 'emoji:read', 'users:read'
89
+ ],
90
+ acceptedScopes: [ 'users:read' ]
91
+ }
92
+ })
93
+
94
+ const buildSlackMessage = event => ({
95
+ ack: () => {},
96
+ envelope_id: 'e9b92395-bedd-4da7',
97
+ body: {
98
+ token: 'ASDFASFASDFASDFASDFASD',
99
+ team_id: 'TASDASDFSDD',
100
+ context_team_id: 'TASDASDFSDD',
101
+ context_enterprise_id: null,
102
+ api_app_id: 'ASADFASDFASDDDS',
103
+ event: {
104
+ client_msg_id: event?.messageId ?? 'be201e20-d035-44dc-a0e2',
105
+ type: event?.type ?? 'message',
106
+ text: event?.text ?? `<@${BOT_ID}> help`,
107
+ user: event?.user ?? 'U123ABC456',
108
+ ts: event?.ts ?? '1688420854.574989',
109
+ blocks: event?.blocks ?? [],
110
+ team: event?.team ?? 'TASDASDFSDD',
111
+ channel: event?.channel ?? 'CASDFASDFASDSD',
112
+ event_ts: event?.ts ?? '1688420854.574989',
113
+ channel_type: event.channel_type ?? 'channel'
114
+ },
115
+ type: 'event_callback',
116
+ event_id: 'Ev05F9TNCNDB',
117
+ event_time: 1688420854,
118
+ authorizations: [ [Object] ],
119
+ is_ext_shared_channel: false,
120
+ event_context: '4-eyJldCI6Im1lc3NhZ2UiLCJ0aWQiOiJUMDQwQ1NOMzQiLCJhaWQiOiJBMDVDWlEzTkNQNCIsImNpZCI6IkNEVUVZNkExVyJ9'
121
+ },
122
+ event: {
123
+ client_msg_id: event?.messageId ?? 'be201e20-d035-44dc-a0e2-befc2b5bd13d',
124
+ type: event?.type ?? 'message',
125
+ text: event?.text ?? `<@${BOT_ID}> help`,
126
+ user: event?.user ?? 'U123ABC456',
127
+ ts: event?.ts ?? '1688420854.574989',
128
+ blocks: event?.blocks ?? [],
129
+ team: event?.team ?? 'TASDASDFSDD',
130
+ channel: event?.channel ?? 'CASDFASDFASDSD',
131
+ event_ts: event?.ts ?? '1688420854.574989',
132
+ channel_type: event.channel_type ?? 'channel'
133
+ },
134
+ retry_num: 0,
135
+ retry_reason: '',
136
+ accepts_response_payload: false
137
+ })
138
+
139
+ const authenticatedPerson = obj => ({
140
+ installed_team_id: 'T12345678',
141
+ team: {
142
+ id: 'T12345678',
143
+ name: 'Team Name'
144
+ },
145
+ self: {
146
+ id: BOT_ID,
147
+ token: 'fake-token',
148
+ }
149
+ })
150
+
151
+ const token = 'some-fake-token'
152
+ const makeRobot = (delegate, webClientMock = {}) => {
153
+ const robot = new Robot('Slack', false, 'hubot')
154
+ robot.brain.data.users['U123ABC456'] = {
155
+ id: 'U123ABC456',
156
+ name: 'hubot',
157
+ room: 'CASDFASDFASDSD'
158
+ }
159
+
160
+ robot.adapter = new SlackAdapter(robot, new SlackClientMock(token, true, delegate), Object.assign(webClientMock, {
161
+ chat: {
162
+ async postMessage({channel, text}) {}
163
+ },
164
+ auth: {
165
+ test() {
166
+ return {
167
+ ok: true,
168
+ url: 'https://someteam.slack.com/',
169
+ team: 'some team',
170
+ user: 'hubot',
171
+ team_id: 'ASDFSDD',
172
+ user_id: BOT_ID,
173
+ bot_id: 'ISIDUUDFIID',
174
+ is_enterprise_install: false,
175
+ response_metadata: {
176
+ scopes: [
177
+ 'app_mentions:read', 'channels:history',
178
+ 'channels:join', 'channels:read',
179
+ 'channels:write.topic', 'chat:write',
180
+ 'commands', 'files:read',
181
+ 'files:write', 'groups:history',
182
+ 'users:write', 'links:read',
183
+ 'im:history', 'im:read',
184
+ 'reactions:read', 'reactions:write',
185
+ 'groups:read', 'mpim:read',
186
+ 'emoji:read', 'users:read',
187
+ 'mpim:history'
188
+ ]
189
+ }
190
+ }
191
+ }
192
+ }
193
+ }), { token })
194
+ return robot
195
+ }
196
+
197
+ describe('Adapter', async () => {
198
+ let robot = null
199
+ beforeEach(async () => {
200
+ robot = makeRobot()
201
+ })
202
+ it('should emit "connected" after calling run', (t, done) => {
203
+ robot.adapter.on('connected', () => {
204
+ robot.shutdown()
205
+ assert.ok(true)
206
+ done()
207
+ })
208
+ robot.run()
209
+ })
210
+ })
211
+
212
+ describe('Authenticate', async () => {
213
+ let robot = null
214
+ beforeEach(async () => {
215
+ robot = makeRobot({
216
+ async start(adapter) {
217
+ adapter.emit('authenticated', new Error('Not authenticated'))
218
+ }
219
+ })
220
+ })
221
+ it('should not be authenticated', (t, done) => {
222
+ robot.adapter.on('authenticated', (err, identiy) => {
223
+ robot.shutdown()
224
+ assert.ok(err)
225
+ assert.deepEqual(err.message, 'Not authenticated')
226
+ done()
227
+ })
228
+ robot.run()
229
+ })
230
+ })
231
+
232
+ describe('Listen to messages', async () => {
233
+ let robot = null
234
+ beforeEach(async () => {
235
+ robot = makeRobot({
236
+ async start(adapter) {
237
+ adapter.emit('authenticated', authenticatedPerson(), null)
238
+ }
239
+ })
240
+ robot.run()
241
+ })
242
+ afterEach(async () => {
243
+ robot.shutdown()
244
+ })
245
+ it('should listen to a message', (t, done) => {
246
+ robot.listen(message => {
247
+ if(message instanceof TextMessage) {
248
+ assert.deepEqual(message.text, 'Hello world')
249
+ done()
250
+ return true
251
+ }
252
+ assert.fail('expect a message')
253
+ },
254
+ {id: 'message listener'},
255
+ res => {
256
+ res.send('hi')
257
+ assert.ok(true, 'should be called for message')
258
+ })
259
+ robot.adapter.mapToHubotMessage(buildSlackMessage({
260
+ "type": "message",
261
+ "channel": "C123ABC456",
262
+ "user": "U123ABC456",
263
+ "text": "Hello world",
264
+ "ts": "1355517523.000005"
265
+ }).event).then(message => {
266
+ robot.receive(message)
267
+ }).catch(e => console.error)
268
+ })
269
+ it('should hear a message, which uses regex for matching', (t, done) => {
270
+ robot.hear(/hello/i, {id: 'message listener'},
271
+ context => {
272
+ assert.deepEqual(context.message.text, 'hello')
273
+ done()
274
+ return true
275
+ }
276
+ )
277
+ robot.adapter.mapToHubotMessage(buildSlackMessage({
278
+ "type": "message",
279
+ "channel": "C123ABC456",
280
+ "user": "U123ABC456",
281
+ "text": `hello`,
282
+ "ts": "1355517523.000005"
283
+ }).event).then(message => {
284
+ robot.receive(message)
285
+ }).catch(e =>console.error)
286
+ })
287
+ })
288
+
289
+ describe('Send messages back', async () => {
290
+ it('should reply to a message that was sent to Hubot', (t, done) => {
291
+ const robot = makeRobot({
292
+ async start(adapter) {
293
+ adapter.emit('authenticated', authenticatedPerson(), null)
294
+ }
295
+ }, {
296
+ chat: {
297
+ async postMessage({ channel, text }) {
298
+ assert.deepEqual(channel, 'C123ABC456')
299
+ assert.deepEqual(text, 'hi')
300
+ robot.shutdown()
301
+ done()
302
+ }
303
+ },
304
+ users: {
305
+ info(params) {
306
+ return buildATestUser()
307
+ }
308
+ },
309
+ auth: {
310
+ test() {
311
+ return {
312
+ user_id: BOT_ID,
313
+ user: 'hubot',
314
+ team_id: 'T12345678',
315
+ team: 'Team Name'
316
+ }
317
+ }
318
+ }
319
+ })
320
+ robot.run()
321
+
322
+ robot.respond(/hello/i, {id: 'message responder'},
323
+ context => {
324
+ assert.deepEqual(context.message.text, '@hubot hello')
325
+ context.reply('hi')
326
+ done()
327
+ robot.shutdown()
328
+ })
329
+ robot.adapter.on('connected', () => {
330
+ robot.adapter.mapToHubotMessage(buildSlackMessage({
331
+ "type": "message",
332
+ "channel": "C123ABC456",
333
+ "user": "U123ABC456",
334
+ "text": `<@${BOT_ID}> hello`,
335
+ "ts": "1355517523.000005"
336
+ }).event).then(message => {
337
+ message.text = robot.adapter.replaceBotIdWithName(message)
338
+ robot.receive(message)
339
+ }).catch(e => console.error)
340
+ })
341
+ })
342
+ })