@hubot-friends/hubot-slack 3.1.3 → 3.2.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.
@@ -16,8 +16,7 @@ jobs:
16
16
  strategy:
17
17
  matrix:
18
18
  node-version:
19
- - '20.x'
20
- - '22.x'
19
+ - '23.x'
21
20
  steps:
22
21
  - name: Checkout
23
22
  uses: actions/checkout@v4
@@ -41,8 +40,7 @@ jobs:
41
40
  strategy:
42
41
  matrix:
43
42
  node-version:
44
- - '20.x'
45
- - '22.x'
43
+ - '23.x'
46
44
  steps:
47
45
  - name: Checkout
48
46
  uses: actions/checkout@v4
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hubot-friends/hubot-slack",
3
- "version": "3.1.3",
3
+ "version": "3.2.0",
4
4
  "description": "A new Slack adapter for Hubot",
5
5
  "homepage": "https://github.com/hubot-friends/hubot-slack#readme",
6
6
  "main": "./index.mjs",
package/src/Bot.mjs CHANGED
@@ -9,10 +9,10 @@ class SlackClient {
9
9
  process.env.HUBOT_SLACK_CONVERSATION_CACHE_TTL_MS
10
10
  ? parseInt(process.env.HUBOT_SLACK_CONVERSATION_CACHE_TTL_MS, 10)
11
11
  : (5 * 60 * 1000);
12
- constructor(options, robot) {
12
+ constructor(options, robot, socket, web) {
13
13
  this.robot = robot;
14
- this.socket = new SocketModeClient({ appToken: options.appToken, ...options.socketModeOptions });
15
- this.web = new WebClient(options.botToken, { maxRequestConcurrency: 1, logLevel: 'error'});
14
+ this.socket = socket ?? new SocketModeClient({ appToken: options.appToken, ...options.socketModeOptions });
15
+ this.web = web ?? new WebClient(options.botToken, { maxRequestConcurrency: 1, logLevel: 'error'});
16
16
  this.apiPageSize = 100;
17
17
  if (!isNaN(options.apiPageSize)) {
18
18
  this.apiPageSize = parseInt(options.apiPageSize, 10);
@@ -72,26 +72,24 @@ class SlackClient {
72
72
  }
73
73
  async send(envelope, message) {
74
74
  const room = envelope.room || envelope.id;
75
+ const thread_ts = envelope.message?.thread_ts
75
76
  if (room == null) {
76
77
  this.robot.logger.error("Cannot send message without a valid room. Envelopes should contain a room property set to a Slack conversation ID.");
77
78
  return;
78
79
  }
79
80
  this.robot.logger.debug(`SlackClient#send() room: ${room}, message: ${message}`);
80
- if (typeof message !== "string") {
81
- message.channel = room
82
- try {
83
- const result = await this.web.chat.postMessage(message)
84
- this.robot.logger.debug(`Successfully sent message to ${room}`)
85
- } catch (e) {
86
- this.robot.logger.error(e, `SlackClient#send(message) error: ${e.message}`)
87
- }
88
- } else {
89
- try {
90
- const result = await this.web.chat.postMessage({ channel: room, text: message })
91
- this.robot.logger.debug(`Successfully sent message (string) to ${room}`)
92
- } catch (e) {
93
- this.robot.logger.error(e, `SlackClient#send(string) error: ${e.message}`)
94
- }
81
+ const messageOptions = {
82
+ channel: room,
83
+ text: typeof message === "string" ? message : message.text,
84
+ thread_ts, // Include thread_ts if it's defined
85
+ ...message, // Spread other properties from the message if it's an object
86
+ };
87
+
88
+ try {
89
+ const result = await this.web.chat.postMessage(messageOptions);
90
+ this.robot.logger.debug(`Successfully sent message to ${room}`);
91
+ } catch (e) {
92
+ this.robot.logger.error(e, `SlackClient#send() error: ${e.message}`);
95
93
  }
96
94
  }
97
95
  loadUsers(callback) {
@@ -254,6 +252,7 @@ class SlackBot extends Adapter {
254
252
  * @param {...(string|Object)} messages - fully documented in SlackClient
255
253
  */
256
254
  async send(envelope, ...messages) {
255
+
257
256
  this.robot.logger.debug('Sending message to Slack');
258
257
  let callback = function() {};
259
258
  if (typeof(messages[messages.length - 1]) === "function") {
package/test/Bot.mjs CHANGED
@@ -191,6 +191,44 @@ describe('Send Messages', () => {
191
191
  assert.deepEqual(stubs._sendCount, 1)
192
192
  assert.deepEqual(stubs._msg, 'message with a callback')
193
193
  })
194
+
195
+ it('envelope thread_ts should be undefined', () => {
196
+ slackbot.client.send = (envelope, message) => {
197
+ stubs._sendCount++;
198
+ stubs._msg = message;
199
+ stubs._envelope = envelope;
200
+ }
201
+ const fakeEnvelope = {
202
+ room: stubs.channel.id,
203
+ user: stubs.user
204
+ }
205
+ slackbot.send(fakeEnvelope, 'message');
206
+ assert.deepEqual(stubs._sendCount, 1);
207
+ assert.deepEqual(stubs._msg, 'message');
208
+ assert.strictEqual(stubs._envelope.message, undefined);
209
+ });
210
+
211
+ it('Should send a message with thread_ts when message is included in envelope', () => {
212
+ slackbot.client.send = (envelope, message) => {
213
+ stubs._sendCount++;
214
+ stubs._msg = message;
215
+ stubs._envelope = envelope;
216
+ }
217
+ const fakeEnvelope = {
218
+ room: stubs.channel.id,
219
+ user: stubs.user,
220
+ message: {
221
+ room: stubs.channel.id,
222
+ user: stubs.user,
223
+ thread_ts: '1234567890.123456'
224
+ }
225
+ }
226
+ slackbot.send(fakeEnvelope, 'message');
227
+ assert.deepEqual(stubs._sendCount, 1);
228
+ assert.deepEqual(stubs._msg, 'message');
229
+ assert.strictEqual(stubs._envelope.message.thread_ts, '1234567890.123456');
230
+ });
231
+
194
232
  })
195
233
 
196
234
  describe('Reply to Messages', () => {
@@ -0,0 +1,100 @@
1
+ import { describe, it, beforeEach } from 'node:test'
2
+ import assert from 'node:assert/strict'
3
+ import { SlackBot, SlackClient } from '../src/Bot.mjs'
4
+ import hubotSlackMock from '../index.mjs'
5
+ import { loadBot, Robot } from 'hubot'
6
+ import { SlackTextMessage, ReactionMessage, FileSharedMessage } from '../src/Message.mjs'
7
+ import EventEmitter from 'node:events'
8
+
9
+ class SocketModeClientMock extends EventEmitter{
10
+ constructor() {
11
+ super()
12
+ }
13
+
14
+ send() {
15
+ return Promise.resolve()
16
+ }
17
+ }
18
+
19
+ class WebClientMock extends EventEmitter{
20
+ constructor(api) {
21
+ super()
22
+ Object.keys(api).forEach((key) => {
23
+ this[key] = api[key]
24
+ })
25
+ }
26
+ }
27
+
28
+ describe('thread_ts should pass thru', () => {
29
+ it('Message should include thread_ts if present', async () => {
30
+ const user = { id
31
+ : 'U12345678', name: 'testuser' }
32
+ const text = 'Hello, world!'
33
+ const rawText = 'Hello, world!'
34
+ const rawMessage = {
35
+ type: 'message',
36
+ user: user.id,
37
+ text: rawText,
38
+ ts: '1234567890.123456',
39
+ thread_ts: '1234567890.123456'
40
+ }
41
+ const channel_id = 'C12345678'
42
+ const robot_name = 'testbot'
43
+ const robot_alias = 'testbot_alias'
44
+ const envelope = {
45
+ user: user,
46
+ message: rawMessage,
47
+ room: channel_id
48
+ }
49
+ const message = {
50
+ text: text,
51
+ user: user.id,
52
+ room: channel_id,
53
+ thread_ts: '1234567890.123456'
54
+ }
55
+ const msg = new SlackTextMessage(user, text, rawText, rawMessage, channel_id, robot_name, robot_alias)
56
+ const sut = new SlackClient({}, new Robot('TestBot', 'testbot', 'testbot_alias'), new SocketModeClientMock(), new WebClientMock({
57
+ chat: {
58
+ postMessage: async (params) => {
59
+ assert.strictEqual(params.thread_ts, '1234567890.123456')
60
+ return { ok: true, ts: '1234567890.123456' }
61
+ }
62
+ }
63
+ }))
64
+ await sut.send(envelope, message)
65
+ })
66
+
67
+ it('Message send a text only message', async () => {
68
+ const user = { id
69
+ : 'U12345678', name: 'testuser' }
70
+ const text = 'Hello, world!'
71
+ const rawText = 'Hello, world!'
72
+ const rawMessage = {
73
+ type: 'message',
74
+ user: user.id,
75
+ text: rawText,
76
+ ts: '1234567890.123456',
77
+ thread_ts: '1234567890.123456'
78
+ }
79
+ const channel_id = 'C12345678'
80
+ const robot_name = 'testbot'
81
+ const robot_alias = 'testbot_alias'
82
+ const envelope = {
83
+ user: user,
84
+ message: rawMessage,
85
+ room: channel_id
86
+ }
87
+ const message = text
88
+ const msg = new SlackTextMessage(user, text, rawText, rawMessage, channel_id, robot_name, robot_alias)
89
+ const sut = new SlackClient({}, new Robot('TestBot', 'testbot', 'testbot_alias'), new SocketModeClientMock(), new WebClientMock({
90
+ chat: {
91
+ postMessage: async (param) => {
92
+ assert.strictEqual(param.text, 'Hello, world!')
93
+ return { ok: true, param }
94
+ }
95
+ }
96
+ }))
97
+ await sut.send(envelope, message)
98
+ })
99
+
100
+ })