@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.
- package/.github/CODE_OF_CONDUCT.md +11 -0
- package/.github/contributing.md +60 -0
- package/.github/issue_template.md +48 -0
- package/.github/maintainers_guide.md +91 -0
- package/.github/pull_request_template.md +8 -0
- package/.github/workflows/ci-build.yml +74 -0
- package/LICENSE +22 -0
- package/README.md +29 -0
- package/docs/Gemfile +2 -0
- package/docs/README.md +15 -0
- package/docs/_config.yml +29 -0
- package/docs/_includes/analytics.html +7 -0
- package/docs/_includes/head.html +33 -0
- package/docs/_includes/page_header.html +20 -0
- package/docs/_includes/side_nav.html +22 -0
- package/docs/_includes/tag_manager.html +13 -0
- package/docs/_layouts/changelog.html +15 -0
- package/docs/_layouts/default.html +47 -0
- package/docs/_layouts/page.html +10 -0
- package/docs/_pages/FAQ.md +63 -0
- package/docs/_pages/about.md +16 -0
- package/docs/_pages/advanced_usage.md +102 -0
- package/docs/_pages/auth.md +44 -0
- package/docs/_pages/basic_usage.md +302 -0
- package/docs/_pages/changelog.html +17 -0
- package/docs/_pages/upgrading.md +49 -0
- package/docs/_posts/2016-07-15-v4.0.0.md +14 -0
- package/docs/_posts/2016-07-19-v4.0.1.md +5 -0
- package/docs/_posts/2016-08-03-v4.0.2.md +4 -0
- package/docs/_posts/2016-09-12-v4.0.3.md +8 -0
- package/docs/_posts/2016-09-12-v4.0.4.md +4 -0
- package/docs/_posts/2016-09-14-v4.0.5.md +4 -0
- package/docs/_posts/2016-09-21-v4.1.0.md +4 -0
- package/docs/_posts/2016-10-12-v4.2.0.md +4 -0
- package/docs/_posts/2016-10-12-v4.2.1.md +4 -0
- package/docs/_posts/2016-11-05-v4.2.2.md +8 -0
- package/docs/_posts/2017-01-05-v4.3.0.md +5 -0
- package/docs/_posts/2017-01-09-v4.3.1.md +5 -0
- package/docs/_posts/2017-02-15-v4.3.2.md +5 -0
- package/docs/_posts/2017-02-17-v4.3.3.md +4 -0
- package/docs/_posts/2017-03-29-v4.3.4.md +5 -0
- package/docs/_posts/2017-08-24-v4.4.0.md +7 -0
- package/docs/_posts/2018-06-08-v4.5.0.md +13 -0
- package/docs/_posts/2018-06-14-v4.5.1.md +4 -0
- package/docs/_posts/2018-07-03-v4.5.2.md +5 -0
- package/docs/_posts/2018-07-17-v4.5.3.md +12 -0
- package/docs/_posts/2018-08-10-v4.5.4.md +7 -0
- package/docs/_posts/2018-10-01-v4.5.5.md +7 -0
- package/docs/_posts/2018-12-21-v4.6.0.md +6 -0
- package/docs/_posts/2019-04-29-v4.7.0.md +6 -0
- package/docs/_posts/2019-04-30-v4.7.1.md +5 -0
- package/docs/_posts/2020-04-03-v4.7.2.md +6 -0
- package/docs/_posts/2020-05-19-v4.8.0.md +10 -0
- package/docs/_posts/2020-10-19-v4.8.1.md +7 -0
- package/docs/_posts/2021-01-26-v4.9.0.md +8 -0
- package/docs/_posts/2022-01-12-v4.10.0.md +8 -0
- package/docs/index.md +93 -0
- package/docs/styles/docs.css +37 -0
- package/package.json +50 -0
- package/slack.js +20 -0
- package/src/SlackAdapter.mjs +302 -0
- package/src/SlackAdapter.test.mjs +342 -0
- package/src/bot.js +526 -0
- package/src/client.js +445 -0
- package/src/extensions.js +82 -0
- package/src/mention.js +15 -0
- package/src/message.js +307 -0
- package/src/testing.mjs +24 -0
- package/test/bot.js +769 -0
- package/test/client.js +446 -0
- package/test/message.js +329 -0
- package/test/stubs.js +388 -0
package/src/message.js
ADDED
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
const {Message, TextMessage} = require.main.require("hubot/es2015.js");
|
|
2
|
+
const SlackClient = require("./client");
|
|
3
|
+
const SlackMention = require("./mention");
|
|
4
|
+
|
|
5
|
+
class ReactionMessage extends Message {
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Represents a message generated by an emoji reaction event
|
|
9
|
+
*
|
|
10
|
+
* @constructor
|
|
11
|
+
* @param {string} type - A String indicating 'reaction_added' or 'reaction_removed'
|
|
12
|
+
* @param {User} user - A User instance that reacted to the item.
|
|
13
|
+
* @param {string} reaction - A String identifying the emoji reaction.
|
|
14
|
+
* @param {Object} item - An Object identifying the target message, file, or comment item.
|
|
15
|
+
* @param {User} [item_user] - A String indicating the user that posted the item. If the item was created by a
|
|
16
|
+
* custom integration (not part of a Slack app with a bot user), then this value will be undefined.
|
|
17
|
+
* @param {string} event_ts - A String of the reaction event timestamp.
|
|
18
|
+
*/
|
|
19
|
+
constructor(type, user, reaction, item_user, item, event_ts) {
|
|
20
|
+
super(user);
|
|
21
|
+
this.type = type;
|
|
22
|
+
this.user = user;
|
|
23
|
+
this.reaction = reaction;
|
|
24
|
+
this.item_user = item_user;
|
|
25
|
+
this.item = item;
|
|
26
|
+
this.event_ts = event_ts;
|
|
27
|
+
this.type = this.type.replace("reaction_", "");
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
class FileSharedMessage extends Message {
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Represents a message generated by an file_shared event
|
|
35
|
+
*
|
|
36
|
+
* @constructor
|
|
37
|
+
* @param {User} user - A User instance that reacted to the item.
|
|
38
|
+
* @param {string} file_id - A String identifying the file_id of the file that was shared.
|
|
39
|
+
* @param {string} event_ts - A String of the file_shared event timestamp.
|
|
40
|
+
*/
|
|
41
|
+
constructor(user, file_id, event_ts) {
|
|
42
|
+
super(user);
|
|
43
|
+
this.user = user;
|
|
44
|
+
this.file_id = file_id;
|
|
45
|
+
this.event_ts = event_ts;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
class MeMessage extends TextMessage {
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Represents a message generated by a /me invocation
|
|
53
|
+
*
|
|
54
|
+
* @constructor
|
|
55
|
+
* @param {User} user - A User instance that created the /me message
|
|
56
|
+
* @param {string} text - Text of the /me message
|
|
57
|
+
* @param {string} event_ts - event timestamp
|
|
58
|
+
*/
|
|
59
|
+
constructor(user, text, event_ts) {
|
|
60
|
+
super(this.user, this.text, this.event_ts);
|
|
61
|
+
this.user = user;
|
|
62
|
+
this.text = text;
|
|
63
|
+
this.event_ts = event_ts;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
class SlackTextMessage extends TextMessage {
|
|
68
|
+
/**
|
|
69
|
+
* Represents a TextMessage created from the Slack adapter
|
|
70
|
+
*
|
|
71
|
+
* @constructor
|
|
72
|
+
* @param {User} user - The User who sent this message
|
|
73
|
+
* @param {string|undefined} text - The parsed message text. Its no longer recommended to use this property.
|
|
74
|
+
* The `buildText()` method can be used to parse the raw text and populate the `text` property.
|
|
75
|
+
* @param {string|undefined} rawText - The unparsed message text. Its no longer recommended to use this property.
|
|
76
|
+
* The constructor will default to the `rawMessage.text` value.
|
|
77
|
+
* @param {Object} rawMessage - The Slack Message object
|
|
78
|
+
* @param {string} rawMessage.text
|
|
79
|
+
* @param {string} rawMessage.ts
|
|
80
|
+
* @param {string} [rawMessage.thread_ts] - the identifier for the thread the message is a part of
|
|
81
|
+
* @param {string} [rawMessage.attachments] - Slack message attachments
|
|
82
|
+
* @param {string} channel_id - The conversation where this message was sent.
|
|
83
|
+
* @param {string} robot_name - The Slack username for this robot
|
|
84
|
+
* @param {string} robot_alias - The alias for this robot
|
|
85
|
+
*/
|
|
86
|
+
constructor(user, text, rawText, rawMessage, channel_id, robot_name, robot_alias) {
|
|
87
|
+
super(user, text, rawMessage.ts);
|
|
88
|
+
// private instance properties
|
|
89
|
+
this.rawMessage = rawMessage;
|
|
90
|
+
this._channel_id = channel_id;
|
|
91
|
+
this._robot_name = robot_name;
|
|
92
|
+
this._robot_alias = robot_alias;
|
|
93
|
+
|
|
94
|
+
// public instance property initialization
|
|
95
|
+
this.rawText = rawText || this.rawMessage.text;
|
|
96
|
+
if (this.rawMessage.thread_ts != null) { this.thread_ts = this.rawMessage.thread_ts; }
|
|
97
|
+
this.mentions = [];
|
|
98
|
+
}
|
|
99
|
+
static MESSAGE_REGEX = new RegExp(`\
|
|
100
|
+
<\
|
|
101
|
+
([@#!])?\
|
|
102
|
+
([^>|]+)\
|
|
103
|
+
(?:\\|\
|
|
104
|
+
([^>]+)\
|
|
105
|
+
)?\
|
|
106
|
+
>\
|
|
107
|
+
`, 'g');
|
|
108
|
+
static MESSAGE_RESERVED_KEYWORDS = ["channel","group","everyone","here"];
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Build the text property, a flat string representation of the contents of this message.
|
|
112
|
+
*
|
|
113
|
+
* @private
|
|
114
|
+
* @param {SlackClient} client - a client that can be used to get more data needed to build the text
|
|
115
|
+
* @param {function} cb - callback for the result
|
|
116
|
+
*/
|
|
117
|
+
buildText(client, cb) {
|
|
118
|
+
// base text
|
|
119
|
+
let text = (this.rawMessage.text != null) ? this.rawMessage.text : "";
|
|
120
|
+
|
|
121
|
+
// flatten any attachments into text
|
|
122
|
+
if (this.rawMessage.attachments) {
|
|
123
|
+
const attachment_text = this.rawMessage.attachments.map(a => a.fallback).join("\n");
|
|
124
|
+
if (attachment_text.length > 0) { text = text + "\n" + attachment_text; }
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Replace links in text async to fetch user and channel info (if present)
|
|
128
|
+
const mentionFormatting = this.replaceLinks(client, text);
|
|
129
|
+
// Fetch conversation info
|
|
130
|
+
const fetchingConversationInfo = client.fetchConversation(this._channel_id);
|
|
131
|
+
return Promise.all([mentionFormatting, fetchingConversationInfo])
|
|
132
|
+
.then(results => {
|
|
133
|
+
const [ replacedText, conversationInfo ] = results;
|
|
134
|
+
text = replacedText;
|
|
135
|
+
text = text.replace(/</g, "<");
|
|
136
|
+
text = text.replace(/>/g, ">");
|
|
137
|
+
text = text.replace(/&/g, "&");
|
|
138
|
+
|
|
139
|
+
// special handling for message text when inside a DM conversation
|
|
140
|
+
if (conversationInfo.is_im) {
|
|
141
|
+
const startOfText = text.indexOf("@") === 0 ? 1 : 0;
|
|
142
|
+
const robotIsNamed = (text.indexOf(this._robot_name) === startOfText) || (text.indexOf(this._robot_alias) === startOfText);
|
|
143
|
+
// Assume it was addressed to us even if it wasn't
|
|
144
|
+
if (!robotIsNamed) {
|
|
145
|
+
text = `${this._robot_name} ${text}`; // If this is a DM, pretend it was addressed to us
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
this.text = text;
|
|
150
|
+
return cb();
|
|
151
|
+
}).catch(error => {
|
|
152
|
+
client.robot.logger.error(`An error occurred while building text: ${error.message}`);
|
|
153
|
+
return cb(error);
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Replace links inside of text
|
|
159
|
+
*
|
|
160
|
+
* @private
|
|
161
|
+
* @param {SlackClient} client - a client that can be used to get more data needed to build the text
|
|
162
|
+
* @returns {Promise<string>}
|
|
163
|
+
*/
|
|
164
|
+
replaceLinks(client, text) {
|
|
165
|
+
let result;
|
|
166
|
+
const regex = SlackTextMessage.MESSAGE_REGEX;
|
|
167
|
+
regex.lastIndex = 0;
|
|
168
|
+
let cursor = 0;
|
|
169
|
+
const parts = [];
|
|
170
|
+
|
|
171
|
+
while (result = regex.exec(text)) {
|
|
172
|
+
var [m, type, link, label] = result;
|
|
173
|
+
|
|
174
|
+
switch (type) {
|
|
175
|
+
case "@":
|
|
176
|
+
if (label) {
|
|
177
|
+
parts.push(text.slice(cursor, result.index), `@${label}`);
|
|
178
|
+
this.mentions.push(new SlackMention(link, "user", undefined));
|
|
179
|
+
} else {
|
|
180
|
+
parts.push(text.slice(cursor, result.index), this.replaceUser(client, link, this.mentions));
|
|
181
|
+
}
|
|
182
|
+
break;
|
|
183
|
+
|
|
184
|
+
case "#":
|
|
185
|
+
if (label) {
|
|
186
|
+
parts.push(text.slice(cursor, result.index), `\#${label}`);
|
|
187
|
+
this.mentions.push(new SlackMention(link, "conversation", undefined));
|
|
188
|
+
} else {
|
|
189
|
+
parts.push(text.slice(cursor, result.index), this.replaceConversation(client, link, this.mentions));
|
|
190
|
+
}
|
|
191
|
+
break;
|
|
192
|
+
|
|
193
|
+
case "!":
|
|
194
|
+
if (SlackTextMessage.MESSAGE_RESERVED_KEYWORDS.includes(link)) {
|
|
195
|
+
parts.push(text.slice(cursor, result.index), `@${link}`);
|
|
196
|
+
} else if (label) {
|
|
197
|
+
parts.push(text.slice(cursor, result.index), label);
|
|
198
|
+
} else {
|
|
199
|
+
parts.push(text.slice(cursor, result.index), m);
|
|
200
|
+
}
|
|
201
|
+
break;
|
|
202
|
+
|
|
203
|
+
default:
|
|
204
|
+
link = link.replace(/^mailto:/, "");
|
|
205
|
+
if (label && (-1 === link.indexOf(label))) {
|
|
206
|
+
parts.push(text.slice(cursor, result.index), `${label} (${link})`);
|
|
207
|
+
} else {
|
|
208
|
+
parts.push(text.slice(cursor, result.index), link);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
cursor = regex.lastIndex;
|
|
213
|
+
if (result[0].length === 0) {
|
|
214
|
+
regex.lastIndex++;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
parts.push(text.slice(cursor));
|
|
219
|
+
|
|
220
|
+
return Promise.all(parts)
|
|
221
|
+
.then(substrings => substrings.join(""));
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Creates a mention from a user ID
|
|
226
|
+
*
|
|
227
|
+
* @private
|
|
228
|
+
* @param {SlackClient} client - a client that can be used to get more data needed to build the text
|
|
229
|
+
* @param {string} id - the user ID
|
|
230
|
+
* @param {Array<Mention>} mentions - a mentions array that is updated to include this user mention
|
|
231
|
+
* @returns {Promise<string>} - a string that can be placed into the text for this mention
|
|
232
|
+
*/
|
|
233
|
+
replaceUser(client, id, mentions) {
|
|
234
|
+
return client.fetchUser(id)
|
|
235
|
+
.then(res => {
|
|
236
|
+
mentions.push(new SlackMention(res.id, "user", res));
|
|
237
|
+
return `@${res.name}`;
|
|
238
|
+
}).catch(error => {
|
|
239
|
+
client.robot.logger.error(`Error getting user info ${id}: ${error.message}`);
|
|
240
|
+
return `<@${id}>`;
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Creates a mention from a conversation ID
|
|
246
|
+
*
|
|
247
|
+
* @private
|
|
248
|
+
* @param {SlackClient} client - a client that can be used to get more data needed to build the text
|
|
249
|
+
* @param {string} id - the conversation ID
|
|
250
|
+
* @param {Array<Mention>} mentions - a mentions array that is updated to include this conversation mention
|
|
251
|
+
* @returns {Promise<string>} - a string that can be placed into the text for this mention
|
|
252
|
+
*/
|
|
253
|
+
replaceConversation(client, id, mentions) {
|
|
254
|
+
return client.fetchConversation(id)
|
|
255
|
+
.then(conversation => {
|
|
256
|
+
if (conversation != null) {
|
|
257
|
+
mentions.push(new SlackMention(conversation.id, "conversation", conversation));
|
|
258
|
+
return `\#${conversation.name}`;
|
|
259
|
+
} else { return `<\#${id}>`; }
|
|
260
|
+
}).catch(error => {
|
|
261
|
+
client.robot.logger.error(`Error getting conversation info ${id}: ${error.message}`);
|
|
262
|
+
return `<\#${id}>`;
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Factory method to construct SlackTextMessage
|
|
268
|
+
* @public
|
|
269
|
+
* @param {User} user - The User who sent this message
|
|
270
|
+
* @param {string|undefined} text - The parsed message text. Its no longer recommended to use this property.
|
|
271
|
+
* The `buildText()` method can be used to parse the raw text and populate the `text` property.
|
|
272
|
+
* @param {string|undefined} rawText - The unparsed message text. Its no longer recommended to use this property.
|
|
273
|
+
* The constructor will default to the `rawMessage.text` value.
|
|
274
|
+
* @param {Object} rawMessage - The Slack Message object
|
|
275
|
+
* @param {string} rawMessage.text
|
|
276
|
+
* @param {string} rawMessage.ts
|
|
277
|
+
* @param {string} [rawMessage.thread_ts] - the identifier for the thread the message is a part of
|
|
278
|
+
* @param {string} [rawMessage.attachments] - Slack message attachments
|
|
279
|
+
* @param {string} channel_id - The conversation where this message was sent.
|
|
280
|
+
* @param {string} robot_name - The Slack username for this robot
|
|
281
|
+
* @param {string} robot_alias - The alias for this robot
|
|
282
|
+
* @param {SlackClient} client - client used to fetch more data
|
|
283
|
+
* @param {function} cb - callback to return the result
|
|
284
|
+
*/
|
|
285
|
+
static makeSlackTextMessage(user, text, rawText, rawMessage, channel_id, robot_name, robot_alias, client, cb) {
|
|
286
|
+
const message = new SlackTextMessage(user, text, rawText, rawMessage, channel_id, robot_name, robot_alias);
|
|
287
|
+
|
|
288
|
+
// creates a completion function that consistently calls the callback after this function has returned
|
|
289
|
+
const done = message => setImmediate(() => cb(null, message));
|
|
290
|
+
|
|
291
|
+
if ((message.text == null)) {
|
|
292
|
+
return message.buildText(client, function(error) {
|
|
293
|
+
if (error) {
|
|
294
|
+
return cb(error);
|
|
295
|
+
}
|
|
296
|
+
return done(message);
|
|
297
|
+
});
|
|
298
|
+
} else {
|
|
299
|
+
return done(message);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
exports.SlackTextMessage = SlackTextMessage;
|
|
305
|
+
exports.ReactionMessage = ReactionMessage;
|
|
306
|
+
exports.FileSharedMessage = FileSharedMessage;
|
|
307
|
+
exports.MeMessage = MeMessage;
|
package/src/testing.mjs
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { SocketModeClient, LogLevel } from '@slack/socket-mode'
|
|
2
|
+
import { WebClient } from '@slack/web-api'
|
|
3
|
+
const logLevel = LogLevel.DEBUG
|
|
4
|
+
const socketModeClient = new SocketModeClient({
|
|
5
|
+
appToken: process.env.SLACK_APP_TOKEN,
|
|
6
|
+
logLevel,
|
|
7
|
+
// pingPongLoggingEnabled: true,
|
|
8
|
+
// serverPingTimeout: 30000,
|
|
9
|
+
// clientPingTimeout: 5000,
|
|
10
|
+
})
|
|
11
|
+
const webClient = new WebClient(process.env.SLACK_BOT_TOKEN, {
|
|
12
|
+
logLevel,
|
|
13
|
+
})
|
|
14
|
+
const receive = async message => {
|
|
15
|
+
const { body, ack } = message
|
|
16
|
+
console.log('receiveing event', message)
|
|
17
|
+
try {
|
|
18
|
+
await ack()
|
|
19
|
+
} catch (e) {
|
|
20
|
+
console.error(e)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
socketModeClient.on('message', receive)
|
|
24
|
+
await socketModeClient.start()
|