Skip to content

Commit

Permalink
[FIX] Slackbridge private channels (#14273)
Browse files Browse the repository at this point in the history
* Fix SlackBridge private channels

* WIP: fix slackbridge private rooms does not send message back to slack

* Fix lint

* WIP: move slack api calls to a specififc file

* Changed all references directly to the slack api

* Fix reactions to execute unsetReaction always when a reaction was removed

* Add function to retrieve all members of slack channel and remove old code

* Add function to retrieve all members of slack channel and remove old code

* Fix send files from slack to rocket

* Fix lint and remove unused code

* Fix error on slackbridge importer

* Fix roompick in admin page, related to choose room to publish in slackbridge

* Fix callbacks order

* Fixed merge error

* Fixed file upload
  • Loading branch information
Hudell authored and rodrigok committed Apr 27, 2019
1 parent 4cd31dc commit fad728b
Show file tree
Hide file tree
Showing 6 changed files with 252 additions and 137 deletions.
7 changes: 2 additions & 5 deletions app/reactions/server/setReaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,23 +46,20 @@ export function setReaction(room, user, message, reaction, shouldReact) {
}
if (userAlreadyReacted) {
removeUserReaction(message, reaction, user.username);

if (_.isEmpty(message.reactions)) {
delete message.reactions;
if (isTheLastMessage(room, message)) {
Rooms.unsetReactionsInLastMessage(room._id);
}
Messages.unsetReactions(message._id);
callbacks.run('unsetReaction', message._id, reaction);
callbacks.run('afterUnsetReaction', message, { user, reaction, shouldReact });
} else {
Messages.setReactions(message._id, message.reactions);
if (isTheLastMessage(room, message)) {
Rooms.setReactionsInLastMessage(room._id, message);
}
callbacks.run('setReaction', message._id, reaction);
callbacks.run('afterSetReaction', message, { user, reaction, shouldReact });
}
callbacks.run('unsetReaction', message._id, reaction);
callbacks.run('afterUnsetReaction', message, { user, reaction, shouldReact });
} else {
if (!message.reactions) {
message.reactions = {};
Expand Down
126 changes: 64 additions & 62 deletions app/slackbridge/server/RocketAdapter.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import _ from 'underscore';
import util from 'util';
import { Meteor } from 'meteor/meteor';
import { Accounts } from 'meteor/accounts-base';
import { Random } from 'meteor/random';
import { HTTP } from 'meteor/http';
import { callbacks } from '../../callbacks';
import { settings } from '../../settings';
import { Messages, Rooms, Users } from '../../models';
import { createRoom, sendMessage, setUserAvatar } from '../../lib';
import { logger } from './logger';
import _ from 'underscore';
import util from 'util';

export default class RocketAdapter {
constructor(slackBridge) {
Expand Down Expand Up @@ -130,7 +129,7 @@ export default class RocketAdapter {
onMessage(rocketMessage) {
this.slackAdapters.forEach((slack) => {
try {
if (! slack.getSlackChannel(rocketMessage.rid)) {
if (!slack.getSlackChannel(rocketMessage.rid)) {
// This is on a channel that the rocket bot is not subscribed
return;
}
Expand All @@ -147,7 +146,7 @@ export default class RocketAdapter {
}

if (rocketMessage.file) {
return this.processFileShare(rocketMessage);
return this.processFileShare(rocketMessage, slack);
}

// A new message from Rocket.Chat
Expand All @@ -163,7 +162,6 @@ export default class RocketAdapter {

processSendMessage(rocketMessage, slack) {
// Since we got this message, SlackBridge_Out_Enabled is true

if (settings.get('SlackBridge_Out_All') === true) {
slack.postMessage(slack.getSlackChannel(rocketMessage.rid), rocketMessage);
} else {
Expand All @@ -189,35 +187,27 @@ export default class RocketAdapter {
return rocketMessage.attachments.find((attachment) => attachment.title_link && attachment.title_link.indexOf(`/${ fileId }/`) >= 0);
}

getFileDownloadUrl(rocketMessage) {
const attachment = this.getMessageAttachment(rocketMessage);

if (attachment) {
return attachment.title_link;
}
}

processFileShare(rocketMessage) {
if (! settings.get('SlackBridge_FileUpload_Enabled')) {
processFileShare(rocketMessage, slack) {
if (!settings.get('SlackBridge_FileUpload_Enabled')) {
return;
}

if (rocketMessage.file.name) {
let file_name = rocketMessage.file.name;
let fileName = rocketMessage.file.name;
let text = rocketMessage.msg;

const attachment = this.getMessageAttachment(rocketMessage);
if (attachment) {
file_name = Meteor.absoluteUrl(attachment.title_link);
fileName = Meteor.absoluteUrl(attachment.title_link);
if (!text) {
text = attachment.description;
}
}

const message = `${ text } ${ file_name }`;
const message = `${ text } ${ fileName }`;

rocketMessage.msg = message;
this.slack.postMessage(this.slack.getSlackChannel(rocketMessage.rid), rocketMessage);
slack.postMessage(slack.getSlackChannel(rocketMessage.rid), rocketMessage);
}
}

Expand Down Expand Up @@ -251,6 +241,23 @@ export default class RocketAdapter {
return Rooms.findOneByImportId(slackChannelId);
}

getRocketUsers(members, slackChannel) {
const rocketUsers = [];
for (const member of members) {
if (member !== slackChannel.creator) {
const rocketUser = this.findUser(member) || this.addUser(member);
if (rocketUser && rocketUser.username) {
rocketUsers.push(rocketUser.username);
}
}
}
return rocketUsers;
}

getRocketUserCreator(slackChannel) {
return slackChannel.creator ? this.findUser(slackChannel.creator) || this.addUser(slackChannel.creator) : null;
}

addChannel(slackChannelID, hasRetried = false) {
logger.rocket.debug('Adding Rocket.Chat channel from Slack', slackChannelID);
let addedRoom;
Expand All @@ -260,41 +267,32 @@ export default class RocketAdapter {
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);
const slackChannel = slack.slackAPI.getRoomInfo(slackChannelID);
if (slackChannel) {
const members = slack.slackAPI.getMembers(slackChannelID);
if (!members) {
logger.rocket.error('Could not fetch room members');
return;
}

const rocketRoom = Rooms.findOneByName(slackChannel.name);

if (rocketRoom || slackChannel.is_general) {
slackChannel.rocketId = slackChannel.is_general ? 'GENERAL' : rocketRoom._id;
Rooms.addImportIds(slackChannel.rocketId, slackChannel.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;
const rocketUsers = this.getRocketUsers(members, slackChannel);
const rocketUserCreator = this.getRocketUserCreator(slackChannel);

if (!rocketUserCreator) {
logger.rocket.error('Could not fetch room creator information', rocketChannelData.creator);
logger.rocket.error('Could not fetch room creator information', slackChannel.creator);
return;
}

try {
const rocketChannel = createRoom(isGroup ? 'p' : 'c', rocketChannelData.name, rocketUserCreator.username, rocketUsers);
rocketChannelData.rocketId = rocketChannel.rid;
const isPrivate = slackChannel.is_private;
const rocketChannel = createRoom(isPrivate ? 'p' : 'c', slackChannel.name, slackChannel.username, rocketUsers);
rocketChannel.rocketId = rocketChannel.rid;
} catch (e) {
if (!hasRetried) {
logger.rocket.debug('Error adding channel from Slack. Will retry in 1s.', e.message);
Expand All @@ -307,27 +305,30 @@ export default class RocketAdapter {
}

const roomUpdate = {
ts: new Date(rocketChannelData.created * 1000),
ts: new Date(slackChannel.created * 1000),
};

let lastSetTopic = 0;
if (!_.isEmpty(rocketChannelData.topic && rocketChannelData.topic.value)) {
roomUpdate.topic = rocketChannelData.topic.value;
lastSetTopic = rocketChannelData.topic.last_set;
if (slackChannel.topic && slackChannel.topic.value) {
roomUpdate.topic = slackChannel.topic.value;
lastSetTopic = slackChannel.topic.last_set;
}
if (!_.isEmpty(rocketChannelData.purpose && rocketChannelData.purpose.value) && rocketChannelData.purpose.last_set > lastSetTopic) {
roomUpdate.topic = rocketChannelData.purpose.value;

if (slackChannel.purpose && slackChannel.purpose.value && slackChannel.purpose.last_set > lastSetTopic) {
roomUpdate.topic = slackChannel.purpose.value;
}
Rooms.addImportIds(rocketChannelData.rocketId, rocketChannelData.id);
slack.addSlackChannel(rocketChannelData.rocketId, slackChannelID);

Rooms.addImportIds(slackChannel.rocketId, slackChannel.id);
slack.addSlackChannel(slackChannel.rocketId, slackChannelID);
}
addedRoom = Rooms.findOneById(rocketChannelData.rocketId);

addedRoom = Rooms.findOneById(slackChannel.rocketId);
}
});

if (!addedRoom) {
logger.rocket.debug('Channel not added');
}

return addedRoom;
}

Expand All @@ -347,9 +348,9 @@ export default class RocketAdapter {
return;
}

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 user = slack.slackAPI.getUser(slackUserID);
if (user) {
const rocketUserData = user;
const isBot = rocketUserData.is_bot === true;
const email = (rocketUserData.profile && rocketUserData.profile.email) || '';
let existingRocketUser;
Expand Down Expand Up @@ -427,6 +428,7 @@ export default class RocketAdapter {
if (!addedUser) {
logger.rocket.debug('User not added');
}

return addedUser;
}

Expand Down
118 changes: 118 additions & 0 deletions app/slackbridge/server/SlackAPI.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import { HTTP } from 'meteor/http';

export class SlackAPI {

constructor(apiToken) {
this.apiToken = apiToken;
}

getChannels() {
const response = HTTP.get('https://slack.com/api/conversations.list', {
params: {
token: this.apiToken,
types: 'public_channel',
},
});
return response && response.data && Array.isArray(response.data.channels) && response.data.channels.length > 0
? response.data.channels
: [];
}

getGroups() {
const response = HTTP.get('https://slack.com/api/conversations.list', {
params: {
token: this.apiToken,
types: 'private_channel',
},
});
return response && response.data && Array.isArray(response.data.channels) && response.data.channels.length > 0
? response.data.channels
: [];
}

getRoomInfo(roomId) {
const response = HTTP.get('https://slack.com/api/conversations.info', {
params: {
token: this.apiToken,
channel: roomId,
include_num_members: true,
},
});
return response && response.data && response.statusCode === 200 && response.data.ok && response.data.channel;
}

getMembers(channelId) {
const { num_members } = this.getRoomInfo(channelId);
const MAX_MEMBERS_PER_CALL = 100;
let members = [];
let currentCursor = '';
for (let index = 0; index < num_members; index += MAX_MEMBERS_PER_CALL) {
const response = HTTP.get('https://slack.com/api/conversations.members', {
params: {
token: this.apiToken,
channel: channelId,
limit: MAX_MEMBERS_PER_CALL,
cursor: currentCursor,
},
});
if (response && response.data && response.statusCode === 200 && response.data.ok && Array.isArray(response.data.members)) {
members = members.concat(response.data.members);
const hasMoreItems = response.data.response_metadata && response.data.response_metadata.next_cursor;
if (hasMoreItems) {
currentCursor = response.data.response_metadata.next_cursor;
}
}
}
return members;
}

react(data) {
const response = HTTP.post('https://slack.com/api/reactions.add', { params: data });
return response && response.statusCode === 200 && response.data && response.data.ok;
}

removeReaction(data) {
const response = HTTP.post('https://slack.com/api/reactions.remove', { params: data });
return response && response.statusCode === 200 && response.data && response.data.ok;
}

removeMessage(data) {
const response = HTTP.post('https://slack.com/api/chat.delete', { params: data });
return response && response.statusCode === 200 && response.data && response.data.ok;
}

sendMessage(data) {
return HTTP.post('https://slack.com/api/chat.postMessage', { params: data });
}

updateMessage(data) {
const response = HTTP.post('https://slack.com/api/chat.update', { params: data });
return response && response.statusCode === 200 && response.data && response.data.ok;
}

getHistory(family, options) {
const response = HTTP.get(`https://slack.com/api/${ family }.history`, { params: Object.assign({ token: this.apiToken }, options) });
return response && response.data;
}

getPins(channelId) {
const response = HTTP.get('https://slack.com/api/pins.list', {
params: {
token: this.apiToken,
channel: channelId,
},
});
return response && response.data && response.statusCode === 200 && response.data.ok && response.data.items;
}

getUser(userId) {
const response = HTTP.get('https://slack.com/api/users.info', {
params: {
token: this.apiToken,
user: userId,
},
});
return response && response.data && response.statusCode === 200 && response.data.ok && response.data.user;
}

}
Loading

0 comments on commit fad728b

Please sign in to comment.