@hubot-friends/hubot-slack 1.0.11 → 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.
- package/index.js +2 -2
- package/package.json +2 -2
- package/src/bot.js +69 -53
- package/src/message.js +22 -32
- package/test/bot.js +8 -23
- package/test/message.js +106 -137
- package/test/stubs.js +4 -4
- package/src/client.js +0 -445
- package/test/client.js +0 -446
package/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hubot-friends/hubot-slack",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.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.js",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"@slack/web-api": "^6.8.1"
|
|
28
28
|
},
|
|
29
29
|
"peerDependencies": {
|
|
30
|
-
"hubot": ">=
|
|
30
|
+
"hubot": ">= 9.0.0"
|
|
31
31
|
},
|
|
32
32
|
"engines": {
|
|
33
33
|
"node": ">= 18.16.0",
|
package/src/bot.js
CHANGED
|
@@ -20,7 +20,7 @@ class SlackClient {
|
|
|
20
20
|
this.apiPageSize = parseInt(options.apiPageSize, 10);
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
this.robot.logger.debug(`SocketModeClient initialized with options: ${JSON.stringify(options.socketModeOptions)}`);
|
|
23
|
+
this.robot.logger.debug(`SocketModeClient initialized with options: ${JSON.stringify(options.socketModeOptions) ?? ''}`);
|
|
24
24
|
|
|
25
25
|
// Map to convert bot user IDs (BXXXXXXXX) to user representations for events from custom
|
|
26
26
|
// integrations and apps without a bot user
|
|
@@ -58,23 +58,21 @@ class SlackClient {
|
|
|
58
58
|
return this.socket.removeAllListeners();
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
setTopic(conversationId, topic) {
|
|
61
|
+
async setTopic(conversationId, topic) {
|
|
62
62
|
this.robot.logger.debug(`SlackClient#setTopic() with topic ${topic}`);
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
74
|
-
return this.robot.logger.error(error, `Error setting topic in conversation ${conversationId}: ${error.message}`);
|
|
75
|
-
});
|
|
63
|
+
try {
|
|
64
|
+
const res = await this.web.conversations.info({channel: conversationId})
|
|
65
|
+
const conversation = res.channel;
|
|
66
|
+
if (!conversation.is_im && !conversation.is_mpim) {
|
|
67
|
+
return this.web.conversations.setTopic({channel: conversationId, topic});
|
|
68
|
+
} else {
|
|
69
|
+
return this.robot.logger.debug(`Conversation ${conversationId} is a DM or MPDM. These conversation types do not have topics.`);
|
|
70
|
+
}
|
|
71
|
+
} catch (error) {
|
|
72
|
+
this.robot.logger.error(error, `Error setting topic in conversation ${conversationId}: ${error.message}`);
|
|
73
|
+
}
|
|
76
74
|
}
|
|
77
|
-
send(envelope, message) {
|
|
75
|
+
async send(envelope, message) {
|
|
78
76
|
const room = envelope.room || envelope.id;
|
|
79
77
|
if (room == null) {
|
|
80
78
|
this.robot.logger.error("Cannot send message without a valid room. Envelopes should contain a room property set to a Slack conversation ID.");
|
|
@@ -83,13 +81,19 @@ class SlackClient {
|
|
|
83
81
|
this.robot.logger.debug(`SlackClient#send() room: ${room}, message: ${message}`);
|
|
84
82
|
if (typeof message !== "string") {
|
|
85
83
|
message.channel = room
|
|
86
|
-
|
|
84
|
+
try {
|
|
85
|
+
const result = await this.web.chat.postMessage(message)
|
|
87
86
|
this.robot.logger.debug(`Successfully sent message to ${room}`)
|
|
88
|
-
}
|
|
87
|
+
} catch (e) {
|
|
88
|
+
this.robot.logger.error(e, `SlackClient#send(message) error: ${e.message}`)
|
|
89
|
+
}
|
|
89
90
|
} else {
|
|
90
|
-
|
|
91
|
+
try {
|
|
92
|
+
const result = await this.web.chat.postMessage({ channel: room, text: message })
|
|
91
93
|
this.robot.logger.debug(`Successfully sent message (string) to ${room}`)
|
|
92
|
-
}
|
|
94
|
+
} catch (e) {
|
|
95
|
+
this.robot.logger.error(e, `SlackClient#send(string) error: ${e.message}`)
|
|
96
|
+
}
|
|
93
97
|
}
|
|
94
98
|
}
|
|
95
99
|
loadUsers(callback) {
|
|
@@ -121,20 +125,19 @@ class SlackClient {
|
|
|
121
125
|
this.updateUserInBrain(r.user);
|
|
122
126
|
return r.user;
|
|
123
127
|
}
|
|
124
|
-
fetchConversation(conversationId) {
|
|
128
|
+
async fetchConversation(conversationId) {
|
|
125
129
|
const expiration = Date.now() - SlackClient.CONVERSATION_CACHE_TTL_MS;
|
|
126
130
|
if (((this.channelData[conversationId] != null ? this.channelData[conversationId].channel : undefined) != null) &&
|
|
127
131
|
(expiration < (this.channelData[conversationId] != null ? this.channelData[conversationId].updated : undefined))) { return Promise.resolve(this.channelData[conversationId].channel); }
|
|
128
132
|
if (this.channelData[conversationId] != null) { delete this.channelData[conversationId]; }
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
});
|
|
133
|
+
const r = await this.web.conversations.info({channel: conversationId})
|
|
134
|
+
if (r.channel != null) {
|
|
135
|
+
this.channelData[conversationId] = {
|
|
136
|
+
channel: r.channel,
|
|
137
|
+
updated: Date.now()
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
return r.channel;
|
|
138
141
|
}
|
|
139
142
|
updateUserInBrain(event_or_user) {
|
|
140
143
|
let key, value;
|
|
@@ -246,7 +249,7 @@ class SlackBot extends Adapter {
|
|
|
246
249
|
* @param {Object} envelope - fully documented in SlackClient
|
|
247
250
|
* @param {...(string|Object)} messages - fully documented in SlackClient
|
|
248
251
|
*/
|
|
249
|
-
send(envelope, ...messages) {
|
|
252
|
+
async send(envelope, ...messages) {
|
|
250
253
|
this.robot.logger.debug('Sending message to Slack');
|
|
251
254
|
let callback = function() {};
|
|
252
255
|
if (typeof(messages[messages.length - 1]) === "function") {
|
|
@@ -257,7 +260,15 @@ class SlackBot extends Adapter {
|
|
|
257
260
|
// NOTE: perhaps do envelope manipulation here instead of in the client (separation of concerns)
|
|
258
261
|
if (message !== "") { return this.client.send(envelope, message); }
|
|
259
262
|
});
|
|
260
|
-
|
|
263
|
+
let results = [];
|
|
264
|
+
try {
|
|
265
|
+
results = await Promise.all(messagePromises)
|
|
266
|
+
callback(null, null)
|
|
267
|
+
} catch (e) {
|
|
268
|
+
this.robot.logger.error(e);
|
|
269
|
+
callback(e, null);
|
|
270
|
+
}
|
|
271
|
+
return results;
|
|
261
272
|
}
|
|
262
273
|
|
|
263
274
|
/**
|
|
@@ -266,7 +277,7 @@ class SlackBot extends Adapter {
|
|
|
266
277
|
* @param {Object} envelope - fully documented in SlackClient
|
|
267
278
|
* @param {...(string|Object)} messages - fully documented in SlackClient
|
|
268
279
|
*/
|
|
269
|
-
reply(envelope, ...messages) {
|
|
280
|
+
async reply(envelope, ...messages) {
|
|
270
281
|
this.robot.logger.debug('replying to message');
|
|
271
282
|
let callback = function() {};
|
|
272
283
|
if (typeof(messages[messages.length - 1]) === "function") {
|
|
@@ -283,7 +294,15 @@ class SlackBot extends Adapter {
|
|
|
283
294
|
return this.client.send(envelope, message);
|
|
284
295
|
}
|
|
285
296
|
});
|
|
286
|
-
|
|
297
|
+
let results = [];
|
|
298
|
+
try {
|
|
299
|
+
results = await Promise.all(messagePromises)
|
|
300
|
+
callback(null, null)
|
|
301
|
+
} catch (e) {
|
|
302
|
+
this.robot.logger.error(e);
|
|
303
|
+
callback(e, null);
|
|
304
|
+
}
|
|
305
|
+
return results;
|
|
287
306
|
}
|
|
288
307
|
|
|
289
308
|
/**
|
|
@@ -292,10 +311,8 @@ class SlackBot extends Adapter {
|
|
|
292
311
|
* @param {Object} envelope - fully documented in SlackClient
|
|
293
312
|
* @param {...string} strings - strings that will be newline separated and set to the conversation topic
|
|
294
313
|
*/
|
|
295
|
-
setTopic(envelope, ...strings) {
|
|
296
|
-
|
|
297
|
-
// TODO: this will fail if sending an object as a value in strings
|
|
298
|
-
return this.client.setTopic(envelope.room, strings.join("\n"));
|
|
314
|
+
async setTopic(envelope, ...strings) {
|
|
315
|
+
return await this.client.setTopic(envelope.room, strings.join("\n"));
|
|
299
316
|
}
|
|
300
317
|
|
|
301
318
|
/**
|
|
@@ -457,16 +474,13 @@ class SlackBot extends Adapter {
|
|
|
457
474
|
// Hubot expects all user objects to have a room property that is used in the envelope for the message after it
|
|
458
475
|
// is received
|
|
459
476
|
from.room = channel ?? '';
|
|
460
|
-
|
|
461
|
-
// looks like sometimes the profile is not set
|
|
462
|
-
if (from.profile) {
|
|
463
|
-
from.name = from.profile.display_name;
|
|
464
|
-
}
|
|
477
|
+
from.name = from?.profile?.display_name ?? null;
|
|
465
478
|
|
|
466
479
|
// add the bot id to the message if it's a direct message
|
|
467
480
|
message.body.event.text = this.addBotIdToMessage(message.body.event);
|
|
468
481
|
message.body.event.text = this.replaceBotIdWithName(message.body.event);
|
|
469
482
|
this.robot.logger.debug(`Text = ${message.body.event.text}`);
|
|
483
|
+
this.robot.logger.debug(`Event subtype = ${message.body.event?.subtype}`);
|
|
470
484
|
try {
|
|
471
485
|
switch (message.event.type) {
|
|
472
486
|
case "member_joined_channel":
|
|
@@ -474,13 +488,13 @@ class SlackBot extends Adapter {
|
|
|
474
488
|
this.robot.logger.debug(`Received enter message for user: ${from.id}, joining: ${channel}`);
|
|
475
489
|
msg = new EnterMessage(from);
|
|
476
490
|
msg.ts = message.event.ts;
|
|
477
|
-
this.receive(msg);
|
|
491
|
+
await this.receive(msg);
|
|
478
492
|
break;
|
|
479
493
|
case "member_left_channel":
|
|
480
494
|
this.robot.logger.debug(`Received leave message for user: ${from.id}, joining: ${channel}`);
|
|
481
495
|
msg = new LeaveMessage(user);
|
|
482
496
|
msg.ts = message.ts;
|
|
483
|
-
this.receive(msg);
|
|
497
|
+
await this.receive(msg);
|
|
484
498
|
break;
|
|
485
499
|
case "reaction_added": case "reaction_removed":
|
|
486
500
|
// Once again Hubot expects all user objects to have a room property that is used in the envelope for the message
|
|
@@ -494,18 +508,20 @@ class SlackBot extends Adapter {
|
|
|
494
508
|
const item_user = (message.body.event.item_user != null) ? this.robot.brain.userForId(message.body.event.item_user.id, message.body.event.item_user) : {};
|
|
495
509
|
|
|
496
510
|
this.robot.logger.debug(`Received reaction message from: ${from.id}, reaction: ${message.body.event.reaction}, item type: ${message.body.event.item.type}`);
|
|
497
|
-
this.receive(new ReactionMessage(message.body.event.type, from, message.body.event.reaction, item_user, message.body.event.item, message.body.event.event_ts));
|
|
511
|
+
await this.receive(new ReactionMessage(message.body.event.type, from, message.body.event.reaction, item_user, message.body.event.item, message.body.event.event_ts));
|
|
498
512
|
break;
|
|
499
513
|
case "file_shared":
|
|
500
514
|
this.robot.logger.debug(`Received file_shared message from: ${message.body.event.user_id}, file_id: ${message.body.event.file_id}`);
|
|
501
|
-
this.receive(new FileSharedMessage(from, message.body.event.file_id, message.body.event.event_ts));
|
|
515
|
+
await this.receive(new FileSharedMessage(from, message.body.event.file_id, message.body.event.event_ts));
|
|
502
516
|
break;
|
|
503
517
|
default:
|
|
504
518
|
this.robot.logger.debug(`Received generic message: ${message.event.type}`);
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
})
|
|
519
|
+
try {
|
|
520
|
+
const msg = await SlackTextMessage.makeSlackTextMessage(from, null, message?.body?.event.text, message?.body?.event, channel, this.robot.name, this.robot.alias, this.client)
|
|
521
|
+
await this.receive(msg);
|
|
522
|
+
} catch (error) {
|
|
523
|
+
this.robot.logger.error(error, `Dropping message due to error ${error.message}`);
|
|
524
|
+
}
|
|
509
525
|
break;
|
|
510
526
|
}
|
|
511
527
|
} catch (e) {
|
|
@@ -532,5 +548,5 @@ class SlackBot extends Adapter {
|
|
|
532
548
|
return res.members.map((member) => this.client.updateUserInBrain(member));
|
|
533
549
|
}
|
|
534
550
|
}
|
|
535
|
-
|
|
536
|
-
module.exports = SlackBot;
|
|
551
|
+
module.exports.SlackClient = SlackClient;
|
|
552
|
+
module.exports.SlackBot = SlackBot;
|
package/src/message.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const {Message, TextMessage} = require.main.require("hubot/es2015.js");
|
|
2
|
-
const SlackClient = require("./
|
|
1
|
+
const { Message, TextMessage, TopicMessage } = require.main.require("hubot/es2015.js");
|
|
2
|
+
const SlackClient = require("./bot.js");
|
|
3
3
|
const SlackMention = require("./mention");
|
|
4
4
|
|
|
5
5
|
class ReactionMessage extends Message {
|
|
@@ -114,10 +114,9 @@ class SlackTextMessage extends TextMessage {
|
|
|
114
114
|
* @param {SlackClient} client - a client that can be used to get more data needed to build the text
|
|
115
115
|
* @param {function} cb - callback for the result
|
|
116
116
|
*/
|
|
117
|
-
buildText(client
|
|
117
|
+
async buildText(client) {
|
|
118
118
|
// base text
|
|
119
119
|
let text = (this.rawMessage.text != null) ? this.rawMessage.text : "";
|
|
120
|
-
|
|
121
120
|
// flatten any attachments into text
|
|
122
121
|
if (this.rawMessage.attachments) {
|
|
123
122
|
const attachment_text = this.rawMessage.attachments.map(a => a.fallback).join("\n");
|
|
@@ -128,19 +127,18 @@ class SlackTextMessage extends TextMessage {
|
|
|
128
127
|
const mentionFormatting = this.replaceLinks(client, text);
|
|
129
128
|
// Fetch conversation info
|
|
130
129
|
const fetchingConversationInfo = client.fetchConversation(this._channel_id);
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
});
|
|
130
|
+
let results = [];
|
|
131
|
+
try {
|
|
132
|
+
results = await Promise.all([mentionFormatting, fetchingConversationInfo]);
|
|
133
|
+
const [ replacedText, conversationInfo ] = results;
|
|
134
|
+
text = replacedText;
|
|
135
|
+
text = text.replace(/</g, "<");
|
|
136
|
+
text = text.replace(/>/g, ">");
|
|
137
|
+
text = text.replace(/&/g, "&");
|
|
138
|
+
} catch (e) {
|
|
139
|
+
client.robot.logger.error(e, `An error occurred while building text: ${e.message}`);
|
|
140
|
+
}
|
|
141
|
+
return text;
|
|
144
142
|
}
|
|
145
143
|
|
|
146
144
|
/**
|
|
@@ -271,22 +269,14 @@ class SlackTextMessage extends TextMessage {
|
|
|
271
269
|
* @param {SlackClient} client - client used to fetch more data
|
|
272
270
|
* @param {function} cb - callback to return the result
|
|
273
271
|
*/
|
|
274
|
-
static makeSlackTextMessage(user, text, rawText, rawMessage, channel_id, robot_name, robot_alias, client
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
// creates a completion function that consistently calls the callback after this function has returned
|
|
278
|
-
const done = message => setImmediate(() => cb(null, message));
|
|
279
|
-
|
|
280
|
-
if ((message.text == null)) {
|
|
281
|
-
return message.buildText(client, function(error) {
|
|
282
|
-
if (error) {
|
|
283
|
-
return cb(error);
|
|
284
|
-
}
|
|
285
|
-
return done(message);
|
|
286
|
-
});
|
|
287
|
-
} else {
|
|
288
|
-
return done(message);
|
|
272
|
+
static async makeSlackTextMessage(user, text, rawText, rawMessage, channel_id, robot_name, robot_alias, client) {
|
|
273
|
+
if(rawMessage?.subtype) {
|
|
274
|
+
return new TopicMessage(user, rawMessage.text, rawMessage.event_ts)
|
|
289
275
|
}
|
|
276
|
+
const message = new SlackTextMessage(user, text, rawText, rawMessage, channel_id, robot_name, robot_alias);
|
|
277
|
+
if (message.text !== null) return message;
|
|
278
|
+
message.text = await message.buildText(client);
|
|
279
|
+
return message;
|
|
290
280
|
}
|
|
291
281
|
}
|
|
292
282
|
|
package/test/bot.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const {describe, it, beforeEach, before, after} = require('node:test');
|
|
2
2
|
const assert = require('node:assert/strict');
|
|
3
3
|
const Module = require('module');
|
|
4
|
-
const SlackBot = require('../src/bot.js');
|
|
4
|
+
const SlackBot = require('../src/bot.js').SlackBot;
|
|
5
5
|
|
|
6
6
|
const hookModuleToReturnMockFromRequire = (module, mock) => {
|
|
7
7
|
const originalRequire = Module.prototype.require;
|
|
@@ -208,23 +208,6 @@ describe('Send Messages', function() {
|
|
|
208
208
|
});
|
|
209
209
|
});
|
|
210
210
|
|
|
211
|
-
describe('Client sending message', function() {
|
|
212
|
-
let stubs, client;
|
|
213
|
-
beforeEach(function() {
|
|
214
|
-
({stubs, client} = require('./stubs.js')());
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
it('Should append as_user = true', function() {
|
|
218
|
-
client.send({room: stubs.channel.id}, {text: 'foo', user: stubs.user, channel: stubs.channel.id});
|
|
219
|
-
assert.ok(stubs._opts.as_user);
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
it('Should append as_user = true only as a default', function() {
|
|
223
|
-
client.send({room: stubs.channel.id}, {text: 'foo', user: stubs.user, channel: stubs.channel.id, as_user: false});
|
|
224
|
-
assert.deepEqual(stubs._opts.as_user, true);
|
|
225
|
-
});
|
|
226
|
-
});
|
|
227
|
-
|
|
228
211
|
describe('Reply to Messages', function() {
|
|
229
212
|
let stubs, slackbot;
|
|
230
213
|
beforeEach(function() {
|
|
@@ -290,16 +273,18 @@ describe('Setting the channel topic', function() {
|
|
|
290
273
|
({stubs, slackbot} = require('./stubs.js')());
|
|
291
274
|
});
|
|
292
275
|
|
|
293
|
-
it('Should set the topic in channels',
|
|
276
|
+
it('Should set the topic in channels', async () => {
|
|
277
|
+
let wasCalled = false;
|
|
294
278
|
stubs.receiveMock.onTopic = function(topic) {
|
|
295
279
|
assert.deepEqual(topic, 'channel');
|
|
296
|
-
|
|
280
|
+
wasCalled = true;
|
|
297
281
|
};
|
|
298
|
-
slackbot.setTopic({room: stubs.channel.id}, 'channel');
|
|
282
|
+
await slackbot.setTopic({room: stubs.channel.id}, 'channel');
|
|
283
|
+
assert.deepEqual(wasCalled, true);
|
|
299
284
|
});
|
|
300
285
|
|
|
301
|
-
it('Should NOT set the topic in DMs',
|
|
302
|
-
slackbot.setTopic({room: 'D1232'}, 'DM');
|
|
286
|
+
it('Should NOT set the topic in DMs', async () => {
|
|
287
|
+
await slackbot.setTopic({room: 'D1232'}, 'DM');
|
|
303
288
|
assert.equal(stubs._topic, undefined);
|
|
304
289
|
});
|
|
305
290
|
});
|