diff --git a/app/slackbridge/server/RocketAdapter.js b/app/slackbridge/server/RocketAdapter.js index af629d5efe0c..6a0c13ff981b 100644 --- a/app/slackbridge/server/RocketAdapter.js +++ b/app/slackbridge/server/RocketAdapter.js @@ -16,7 +16,7 @@ export default class RocketAdapter { this.slackBridge = slackBridge; this.util = util; this.userTags = {}; - this.slack = {}; + this.slackAdapters = []; } connect() { @@ -27,8 +27,14 @@ export default class RocketAdapter { this.unregisterForEvents(); } - setSlack(slack) { - this.slack = slack; + addSlack(slack) { + if (this.slackAdapters.indexOf(slack) < 0) { + this.slackAdapters.push(slack); + } + } + + clearSlackAdapters() { + this.slackAdapters = []; } registerForEvents() { @@ -48,17 +54,18 @@ export default class RocketAdapter { } onMessageDelete(rocketMessageDeleted) { - try { - if (! this.slack.getSlackChannel(rocketMessageDeleted.rid)) { - // This is on a channel that the rocket bot is not subscribed - return; + this.slackAdapters.forEach((slack) => { + try { + if (!slack.getSlackChannel(rocketMessageDeleted.rid)) { + // This is on a channel that the rocket bot is not subscribed on this slack server + return; + } + logger.rocket.debug('onRocketMessageDelete', rocketMessageDeleted); + slack.postDeleteMessage(rocketMessageDeleted); + } catch (err) { + logger.rocket.error('Unhandled error onMessageDelete', err); } - logger.rocket.debug('onRocketMessageDelete', rocketMessageDeleted); - - this.slack.postDeleteMessage(rocketMessageDeleted); - } catch (err) { - logger.rocket.error('Unhandled error onMessageDelete', err); - } + }); } onSetReaction(rocketMsgID, reaction) { @@ -66,6 +73,7 @@ export default class RocketAdapter { if (!this.slackBridge.isReactionsEnabled) { return; } + logger.rocket.debug('onRocketSetReaction'); if (rocketMsgID && reaction) { @@ -75,11 +83,13 @@ export default class RocketAdapter { } const rocketMsg = Messages.findOneById(rocketMsgID); if (rocketMsg) { - const slackChannel = this.slack.getSlackChannel(rocketMsg.rid); - if (null != slackChannel) { - const slackTS = this.slack.getTimeStamp(rocketMsg); - this.slack.postReactionAdded(reaction.replace(/:/g, ''), slackChannel.id, slackTS); - } + this.slackAdapters.forEach((slack) => { + const slackChannel = slack.getSlackChannel(rocketMsg.rid); + if (null != slackChannel) { + const slackTS = slack.getTimeStamp(rocketMsg); + slack.postReactionAdded(reaction.replace(/:/g, ''), slackChannel.id, slackTS); + } + }); } } } catch (err) { @@ -92,6 +102,7 @@ export default class RocketAdapter { if (!this.slackBridge.isReactionsEnabled) { return; } + logger.rocket.debug('onRocketUnSetReaction'); if (rocketMsgID && reaction) { @@ -102,11 +113,13 @@ export default class RocketAdapter { const rocketMsg = Messages.findOneById(rocketMsgID); if (rocketMsg) { - const slackChannel = this.slack.getSlackChannel(rocketMsg.rid); - if (null != slackChannel) { - const slackTS = this.slack.getTimeStamp(rocketMsg); - this.slack.postReactionRemove(reaction.replace(/:/g, ''), slackChannel.id, slackTS); - } + this.slackAdapters.forEach((slack) => { + const slackChannel = slack.getSlackChannel(rocketMsg.rid); + if (slackChannel != null) { + const slackTS = slack.getTimeStamp(rocketMsg); + slack.postReactionRemove(reaction.replace(/:/g, ''), slackChannel.id, slackTS); + } + }); } } } catch (err) { @@ -115,47 +128,50 @@ export default class RocketAdapter { } onMessage(rocketMessage) { - try { - if (! this.slack.getSlackChannel(rocketMessage.rid)) { - // This is on a channel that the rocket bot is not subscribed - return; - } - logger.rocket.debug('onRocketMessage', rocketMessage); + this.slackAdapters.forEach((slack) => { + try { + if (! slack.getSlackChannel(rocketMessage.rid)) { + // This is on a channel that the rocket bot is not subscribed + return; + } + logger.rocket.debug('onRocketMessage', rocketMessage); - if (rocketMessage.editedAt) { - // This is an Edit Event - this.processMessageChanged(rocketMessage); - return rocketMessage; - } - // Ignore messages originating from Slack - if (rocketMessage._id.indexOf('slack-') === 0) { - return rocketMessage; - } + if (rocketMessage.editedAt) { + // This is an Edit Event + this.processMessageChanged(rocketMessage, slack); + return rocketMessage; + } + // Ignore messages originating from Slack + if (rocketMessage._id.indexOf('slack-') === 0) { + return rocketMessage; + } - if (rocketMessage.file) { - return this.processFileShare(rocketMessage); - } + if (rocketMessage.file) { + return this.processFileShare(rocketMessage); + } - // A new message from Rocket.Chat - this.processSendMessage(rocketMessage); - } catch (err) { - logger.rocket.error('Unhandled error onMessage', err); - } + // A new message from Rocket.Chat + this.processSendMessage(rocketMessage, slack); + + } catch (err) { + logger.rocket.error('Unhandled error onMessage', err); + } + }); return rocketMessage; } - processSendMessage(rocketMessage) { + processSendMessage(rocketMessage, slack) { // Since we got this message, SlackBridge_Out_Enabled is true if (settings.get('SlackBridge_Out_All') === true) { - this.slack.postMessage(this.slack.getSlackChannel(rocketMessage.rid), rocketMessage); + slack.postMessage(slack.getSlackChannel(rocketMessage.rid), rocketMessage); } else { // They want to limit to certain groups const outSlackChannels = _.pluck(settings.get('SlackBridge_Out_Channels'), '_id') || []; // logger.rocket.debug('Out SlackChannels: ', outSlackChannels); if (outSlackChannels.indexOf(rocketMessage.rid) !== -1) { - this.slack.postMessage(this.slack.getSlackChannel(rocketMessage.rid), rocketMessage); + slack.postMessage(slack.getSlackChannel(rocketMessage.rid), rocketMessage); } } } @@ -205,7 +221,7 @@ export default class RocketAdapter { } } - processMessageChanged(rocketMessage) { + processMessageChanged(rocketMessage, slack) { if (rocketMessage) { if (rocketMessage.updatedBySlack) { // We have already processed this @@ -214,8 +230,8 @@ export default class RocketAdapter { } // This was a change from Rocket.Chat - const slackChannel = this.slack.getSlackChannel(rocketMessage.rid); - this.slack.postMessageUpdate(slackChannel, rocketMessage); + const slackChannel = slack.getSlackChannel(rocketMessage.rid); + slack.postMessageUpdate(slackChannel, rocketMessage); } } @@ -237,70 +253,82 @@ export default class RocketAdapter { addChannel(slackChannelID, hasRetried = false) { logger.rocket.debug('Adding Rocket.Chat channel from Slack', slackChannelID); - let slackResults = null; - let isGroup = false; - if (slackChannelID.charAt(0) === 'C') { - slackResults = HTTP.get('https://slack.com/api/channels.info', { params: { token: this.slackBridge.apiToken, channel: slackChannelID } }); - } else if (slackChannelID.charAt(0) === 'G') { - slackResults = HTTP.get('https://slack.com/api/groups.info', { params: { token: this.slackBridge.apiToken, channel: slackChannelID } }); - isGroup = true; - } - if (slackResults && slackResults.data && slackResults.data.ok === true) { - const rocketChannelData = isGroup ? slackResults.data.group : slackResults.data.channel; - const existingRocketRoom = Rooms.findOneByName(rocketChannelData.name); - - // If the room exists, make sure we have its id in importIds - if (existingRocketRoom || rocketChannelData.is_general) { - rocketChannelData.rocketId = rocketChannelData.is_general ? 'GENERAL' : existingRocketRoom._id; - Rooms.addImportIds(rocketChannelData.rocketId, rocketChannelData.id); - } else { - const rocketUsers = []; - for (const member of rocketChannelData.members) { - if (member !== rocketChannelData.creator) { - const rocketUser = this.findUser(member) || this.addUser(member); - if (rocketUser && rocketUser.username) { - rocketUsers.push(rocketUser.username); + let addedRoom; + + this.slackAdapters.forEach((slack) => { + if (addedRoom) { + return; + } + + let slackResults = null; + let isGroup = false; + if (slackChannelID.charAt(0) === 'C') { + slackResults = HTTP.get('https://slack.com/api/channels.info', { params: { token: slack.apiToken, channel: slackChannelID } }); + } else if (slackChannelID.charAt(0) === 'G') { + slackResults = HTTP.get('https://slack.com/api/groups.info', { params: { token: slack.apiToken, channel: slackChannelID } }); + isGroup = true; + } + if (slackResults && slackResults.data && slackResults.data.ok === true) { + const rocketChannelData = isGroup ? slackResults.data.group : slackResults.data.channel; + const existingRocketRoom = Rooms.findOneByName(rocketChannelData.name); + + // If the room exists, make sure we have its id in importIds + if (existingRocketRoom || rocketChannelData.is_general) { + rocketChannelData.rocketId = rocketChannelData.is_general ? 'GENERAL' : existingRocketRoom._id; + Rooms.addImportIds(rocketChannelData.rocketId, rocketChannelData.id); + } else { + const rocketUsers = []; + for (const member of rocketChannelData.members) { + if (member !== rocketChannelData.creator) { + const rocketUser = this.findUser(member) || this.addUser(member); + if (rocketUser && rocketUser.username) { + rocketUsers.push(rocketUser.username); + } } } - } - const rocketUserCreator = rocketChannelData.creator ? this.findUser(rocketChannelData.creator) || this.addUser(rocketChannelData.creator) : null; - if (!rocketUserCreator) { - logger.rocket.error('Could not fetch room creator information', rocketChannelData.creator); - return; - } + const rocketUserCreator = rocketChannelData.creator ? this.findUser(rocketChannelData.creator) || this.addUser(rocketChannelData.creator) : null; + if (!rocketUserCreator) { + logger.rocket.error('Could not fetch room creator information', rocketChannelData.creator); + return; + } - try { - const rocketChannel = createRoom(isGroup ? 'p' : 'c', rocketChannelData.name, rocketUserCreator.username, rocketUsers); - rocketChannelData.rocketId = rocketChannel.rid; - } catch (e) { - if (!hasRetried) { - logger.rocket.debug('Error adding channel from Slack. Will retry in 1s.', e.message); - // If first time trying to create channel fails, could be because of multiple messages received at the same time. Try again once after 1s. - Meteor._sleepForMs(1000); - return this.findChannel(slackChannelID) || this.addChannel(slackChannelID, true); - } else { - console.log(e.message); + try { + const rocketChannel = createRoom(isGroup ? 'p' : 'c', rocketChannelData.name, rocketUserCreator.username, rocketUsers); + rocketChannelData.rocketId = rocketChannel.rid; + } catch (e) { + if (!hasRetried) { + logger.rocket.debug('Error adding channel from Slack. Will retry in 1s.', e.message); + // If first time trying to create channel fails, could be because of multiple messages received at the same time. Try again once after 1s. + Meteor._sleepForMs(1000); + return this.findChannel(slackChannelID) || this.addChannel(slackChannelID, true); + } else { + console.log(e.message); + } } - } - const roomUpdate = { - ts: new Date(rocketChannelData.created * 1000), - }; - let lastSetTopic = 0; - if (!_.isEmpty(rocketChannelData.topic && rocketChannelData.topic.value)) { - roomUpdate.topic = rocketChannelData.topic.value; - lastSetTopic = rocketChannelData.topic.last_set; - } - if (!_.isEmpty(rocketChannelData.purpose && rocketChannelData.purpose.value) && rocketChannelData.purpose.last_set > lastSetTopic) { - roomUpdate.topic = rocketChannelData.purpose.value; + const roomUpdate = { + ts: new Date(rocketChannelData.created * 1000), + }; + let lastSetTopic = 0; + if (!_.isEmpty(rocketChannelData.topic && rocketChannelData.topic.value)) { + roomUpdate.topic = rocketChannelData.topic.value; + lastSetTopic = rocketChannelData.topic.last_set; + } + if (!_.isEmpty(rocketChannelData.purpose && rocketChannelData.purpose.value) && rocketChannelData.purpose.last_set > lastSetTopic) { + roomUpdate.topic = rocketChannelData.purpose.value; + } + Rooms.addImportIds(rocketChannelData.rocketId, rocketChannelData.id); + slack.addSlackChannel(rocketChannelData.rocketId, slackChannelID); } - Rooms.addImportIds(rocketChannelData.rocketId, rocketChannelData.id); - this.slack.addSlackChannel(rocketChannelData.rocketId, slackChannelID); + addedRoom = Rooms.findOneById(rocketChannelData.rocketId); } - return Rooms.findOneById(rocketChannelData.rocketId); + }); + + if (!addedRoom) { + logger.rocket.debug('Channel not added'); } - logger.rocket.debug('Channel not added'); - return; + + return addedRoom; } findUser(slackUserID) { @@ -313,83 +341,93 @@ export default class RocketAdapter { addUser(slackUserID) { logger.rocket.debug('Adding Rocket.Chat user from Slack', slackUserID); - const slackResults = HTTP.get('https://slack.com/api/users.info', { params: { token: this.slackBridge.apiToken, user: slackUserID } }); - if (slackResults && slackResults.data && slackResults.data.ok === true && slackResults.data.user) { - const rocketUserData = slackResults.data.user; - const isBot = rocketUserData.is_bot === true; - const email = (rocketUserData.profile && rocketUserData.profile.email) || ''; - let existingRocketUser; - if (!isBot) { - existingRocketUser = Users.findOneByEmailAddress(email) || Users.findOneByUsername(rocketUserData.name); - } else { - existingRocketUser = Users.findOneByUsername(rocketUserData.name); + let addedUser; + this.slackAdapters.forEach((slack) => { + if (addedUser) { + return; } - if (existingRocketUser) { - rocketUserData.rocketId = existingRocketUser._id; - rocketUserData.name = existingRocketUser.username; - } else { - const newUser = { - password: Random.id(), - username: rocketUserData.name, - }; - - if (!isBot && email) { - newUser.email = email; + const slackResults = HTTP.get('https://slack.com/api/users.info', { params: { token: slack.apiToken, user: slackUserID } }); + if (slackResults && slackResults.data && slackResults.data.ok === true && slackResults.data.user) { + const rocketUserData = slackResults.data.user; + const isBot = rocketUserData.is_bot === true; + const email = (rocketUserData.profile && rocketUserData.profile.email) || ''; + let existingRocketUser; + if (!isBot) { + existingRocketUser = Users.findOneByEmailAddress(email) || Users.findOneByUsername(rocketUserData.name); + } else { + existingRocketUser = Users.findOneByUsername(rocketUserData.name); } - if (isBot) { - newUser.joinDefaultChannels = false; - } + if (existingRocketUser) { + rocketUserData.rocketId = existingRocketUser._id; + rocketUserData.name = existingRocketUser.username; + } else { + const newUser = { + password: Random.id(), + username: rocketUserData.name, + }; + + if (!isBot && email) { + newUser.email = email; + } - rocketUserData.rocketId = Accounts.createUser(newUser); - const userUpdate = { - utcOffset: rocketUserData.tz_offset / 3600, // Slack's is -18000 which translates to Rocket.Chat's after dividing by 3600, - roles: isBot ? ['bot'] : ['user'], - }; + if (isBot) { + newUser.joinDefaultChannels = false; + } - if (rocketUserData.profile && rocketUserData.profile.real_name) { - userUpdate.name = rocketUserData.profile.real_name; - } + rocketUserData.rocketId = Accounts.createUser(newUser); + const userUpdate = { + utcOffset: rocketUserData.tz_offset / 3600, // Slack's is -18000 which translates to Rocket.Chat's after dividing by 3600, + roles: isBot ? ['bot'] : ['user'], + }; - if (rocketUserData.deleted) { - userUpdate.active = false; - userUpdate['services.resume.loginTokens'] = []; - } + if (rocketUserData.profile && rocketUserData.profile.real_name) { + userUpdate.name = rocketUserData.profile.real_name; + } - Users.update({ _id: rocketUserData.rocketId }, { $set: userUpdate }); + if (rocketUserData.deleted) { + userUpdate.active = false; + userUpdate['services.resume.loginTokens'] = []; + } - const user = Users.findOneById(rocketUserData.rocketId); + Users.update({ _id: rocketUserData.rocketId }, { $set: userUpdate }); - let url = null; - if (rocketUserData.profile) { - if (rocketUserData.profile.image_original) { - url = rocketUserData.profile.image_original; - } else if (rocketUserData.profile.image_512) { - url = rocketUserData.profile.image_512; + const user = Users.findOneById(rocketUserData.rocketId); + + let url = null; + if (rocketUserData.profile) { + if (rocketUserData.profile.image_original) { + url = rocketUserData.profile.image_original; + } else if (rocketUserData.profile.image_512) { + url = rocketUserData.profile.image_512; + } } - } - if (url) { - try { - setUserAvatar(user, url, null, 'url'); - } catch (error) { - logger.rocket.debug('Error setting user avatar', error.message); + if (url) { + try { + setUserAvatar(user, url, null, 'url'); + } catch (error) { + logger.rocket.debug('Error setting user avatar', error.message); + } } } - } - const importIds = [rocketUserData.id]; - if (isBot && rocketUserData.profile && rocketUserData.profile.bot_id) { - importIds.push(rocketUserData.profile.bot_id); - } - Users.addImportIds(rocketUserData.rocketId, importIds); - if (!this.userTags[slackUserID]) { - this.userTags[slackUserID] = { slack: `<@${ slackUserID }>`, rocket: `@${ rocketUserData.name }` }; + const importIds = [rocketUserData.id]; + if (isBot && rocketUserData.profile && rocketUserData.profile.bot_id) { + importIds.push(rocketUserData.profile.bot_id); + } + Users.addImportIds(rocketUserData.rocketId, importIds); + if (!this.userTags[slackUserID]) { + this.userTags[slackUserID] = { slack: `<@${ slackUserID }>`, rocket: `@${ rocketUserData.name }` }; + } + addedUser = Users.findOneById(rocketUserData.rocketId); } - return Users.findOneById(rocketUserData.rocketId); + }); + + if (!addedUser) { + logger.rocket.debug('User not added'); } - logger.rocket.debug('User not added'); - return; + return addedUser; } addAliasToMsg(rocketUserName, rocketMsgObj) { @@ -405,11 +443,11 @@ export default class RocketAdapter { return rocketMsgObj; } - createAndSaveMessage(rocketChannel, rocketUser, slackMessage, rocketMsgDataDefaults, isImporting) { + createAndSaveMessage(rocketChannel, rocketUser, slackMessage, rocketMsgDataDefaults, isImporting, slack) { if (slackMessage.type === 'message') { let rocketMsgObj = {}; if (!_.isEmpty(slackMessage.subtype)) { - rocketMsgObj = this.slack.processSubtypedMessage(rocketChannel, rocketUser, slackMessage, isImporting); + rocketMsgObj = slack.processSubtypedMessage(rocketChannel, rocketUser, slackMessage, isImporting); if (!rocketMsgObj) { return; } @@ -440,8 +478,11 @@ export default class RocketAdapter { } if (slackMessage.subtype === 'bot_message') { Meteor.setTimeout(() => { - if (slackMessage.bot_id && slackMessage.ts && !Messages.findOneBySlackBotIdAndSlackTs(slackMessage.bot_id, slackMessage.ts)) { - sendMessage(rocketUser, rocketMsgObj, rocketChannel, true); + if (slackMessage.bot_id && slackMessage.ts) { + // Make sure that a message with the same bot_id and timestamp doesn't already exists + if (!Messages.findOneBySlackBotIdAndSlackTs(slackMessage.bot_id, slackMessage.ts)) { + sendMessage(rocketUser, rocketMsgObj, rocketChannel, true); + } } }, 500); } else { diff --git a/app/slackbridge/server/SlackAdapter.js b/app/slackbridge/server/SlackAdapter.js index 9641fc8acdaf..893262b4e70e 100644 --- a/app/slackbridge/server/SlackAdapter.js +++ b/app/slackbridge/server/SlackAdapter.js @@ -31,6 +31,8 @@ export default class SlackAdapter { // On Slack, a rocket integration bot will be added to slack channels, this is the list of those channels, key is Rocket Ch ID this.slackChannelRocketBotMembershipMap = new Map(); // Key=RocketChannelID, Value=SlackChannel this.rocket = {}; + this.messagesBeingSent = []; + this.slackBotId = false; } /** @@ -626,6 +628,35 @@ export default class SlackAdapter { } } + storeMessageBeingSent(data) { + this.messagesBeingSent.push(data); + } + + removeMessageBeingSent(data) { + const idx = this.messagesBeingSent.indexOf(data); + if (idx >= 0) { + this.messagesBeingSent.splice(idx, 1); + } + } + + isMessageBeingSent(username, channel) { + if (!this.messagesBeingSent.length) { + return false; + } + + return this.messagesBeingSent.some((messageData) => { + if (messageData.username !== username) { + return false; + } + + if (messageData.channel !== channel) { + return false; + } + + return true; + }); + } + postMessage(slackChannel, rocketMessage) { if (slackChannel && slackChannel.id) { let iconUrl = getUserAvatarURL(rocketMessage.u && rocketMessage.u.username); @@ -641,8 +672,20 @@ export default class SlackAdapter { link_names: 1, }; logger.slack.debug('Post Message To Slack', data); + + // If we don't have the bot id yet and we have multiple slack bridges, we need to keep track of the messages that are being sent + if (!this.slackBotId && this.rocket.slackAdapters && this.rocket.slackAdapters.length >= 2) { + this.storeMessageBeingSent(data); + } + const postResult = HTTP.post('https://slack.com/api/chat.postMessage', { params: data }); + + if (!this.slackBotId && this.rocket.slackAdapters && this.rocket.slackAdapters.length >= 2) { + this.removeMessageBeingSent(data); + } + if (postResult.statusCode === 200 && postResult.data && postResult.data.message && postResult.data.message.bot_id && postResult.data.message.ts) { + this.slackBotId = postResult.data.message.bot_id; Messages.setSlackBotIdAndSlackTs(rocketMessage._id, postResult.data.message.bot_id, postResult.data.message.ts); logger.slack.debug(`RocketMsgID=${ rocketMessage._id } SlackMsgID=${ postResult.data.message.ts } SlackBotID=${ postResult.data.message.bot_id }`); } @@ -780,7 +823,7 @@ export default class SlackAdapter { msgDataDefaults.imported = 'slackbridge'; } try { - this.rocket.createAndSaveMessage(rocketChannel, rocketUser, slackMessage, msgDataDefaults, isImporting); + this.rocket.createAndSaveMessage(rocketChannel, rocketUser, slackMessage, msgDataDefaults, isImporting, this); } catch (e) { // http://www.mongodb.org/about/contributors/error-codes/ // 11000 == duplicate key error @@ -799,6 +842,17 @@ export default class SlackAdapter { return; } + if (this.slackBotId) { + if (slackMessage.bot_id === this.slackBotId) { + return; + } + } else { + const slackChannel = this.getSlackChannel(rocketChannel._id); + if (this.isMessageBeingSent(slackMessage.username || slackMessage.bot_id, slackChannel.id)) { + return; + } + } + const rocketMsgObj = { msg: this.rocket.convertSlackMsgTxtToRocketTxtFormat(slackMessage.text), rid: rocketChannel._id, diff --git a/app/slackbridge/server/settings.js b/app/slackbridge/server/settings.js index 4fd7513f331f..06421b512631 100644 --- a/app/slackbridge/server/settings.js +++ b/app/slackbridge/server/settings.js @@ -11,11 +11,13 @@ Meteor.startup(function() { this.add('SlackBridge_APIToken', '', { type: 'string', + multiline: true, enableQuery: { _id: 'SlackBridge_Enabled', value: true, }, - i18nLabel: 'API_Token', + i18nLabel: 'SlackBridge_APIToken', + i18nDescription: 'SlackBridge_APIToken_Description', }); this.add('SlackBridge_FileUpload_Enabled', true, { diff --git a/app/slackbridge/server/slackbridge.js b/app/slackbridge/server/slackbridge.js index f8757a1c234a..953453b874be 100644 --- a/app/slackbridge/server/slackbridge.js +++ b/app/slackbridge/server/slackbridge.js @@ -9,16 +9,15 @@ import { logger } from './logger'; class SlackBridgeClass { constructor() { - this.slack = new SlackAdapter(this); + this.slackAdapters = []; this.rocket = new RocketAdapter(this); this.reactionsMap = new Map(); // Sync object between rocket and slack this.connected = false; - this.rocket.setSlack(this.slack); - this.slack.setRocket(this.rocket); + this.rocket.clearSlackAdapters(); // Settings that we cache versus looking up at runtime - this.apiToken = false; + this.apiTokens = false; this.aliasFormat = ''; this.excludeBotnames = ''; this.isReactionsEnabled = true; @@ -28,8 +27,19 @@ class SlackBridgeClass { connect() { if (this.connected === false) { + this.slackAdapters = []; + this.rocket.clearSlackAdapters(); + + const tokenList = this.apiTokens.split('\n'); + tokenList.forEach((apiToken) => { + const slack = new SlackAdapter(this); + slack.setRocket(this.rocket); + this.rocket.addSlack(slack); + this.slackAdapters.push(slack); + + slack.connect(apiToken); + }); - this.slack.connect(this.apiToken); if (settings.get('SlackBridge_Out_Enabled')) { this.rocket.connect(); } @@ -42,7 +52,10 @@ class SlackBridgeClass { disconnect() { if (this.connected === true) { this.rocket.disconnect(); - this.slack.disconnect(); + this.slackAdapters.forEach((slack) => { + slack.disconnect(); + }); + this.slackAdapters = []; this.connected = false; logger.connection.info('Disabled'); } @@ -51,8 +64,8 @@ class SlackBridgeClass { processSettings() { // Slack installation API token settings.get('SlackBridge_APIToken', (key, value) => { - if (value !== this.apiToken) { - this.apiToken = value; + if (value !== this.apiTokens) { + this.apiTokens = value; if (this.connected) { this.disconnect(); this.connect(); @@ -82,7 +95,7 @@ class SlackBridgeClass { // Is this entire SlackBridge enabled settings.get('SlackBridge_Enabled', (key, value) => { - if (value && this.apiToken) { + if (value && this.apiTokens) { this.connect(); } else { this.disconnect(); diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index dbb540301b92..525e44a9da12 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -2630,6 +2630,8 @@ "Size": "Size", "Skip": "Skip", "Slack_Users": "Slack's Users CSV", + "SlackBridge_APIToken": "API Tokens", + "SlackBridge_APIToken_Description": "You can configure multiple slack servers by adding one API Token per line.", "SlackBridge_error": "SlackBridge got an error while importing your messages at %s: %s", "SlackBridge_finish": "SlackBridge has finished importing the messages at %s. Please reload to view all messages.", "SlackBridge_Out_All": "SlackBridge Out All",