Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FIX] SlackBridge private channels #13261

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
1a960aa
Fix SlackBridge private channels
nylen Jan 25, 2019
ce36615
Merge branch 'develop' into fix/slackbridge-private-channels
nylen Jan 25, 2019
c6b445d
Merge branch 'develop' into fix/slackbridge-private-channels
MarcosSpessatto Apr 10, 2019
ddb73bd
WIP: fix slackbridge private rooms does not send message back to slack
MarcosSpessatto Apr 10, 2019
e732a46
Fix lint
MarcosSpessatto Apr 10, 2019
902cfda
WIP: move slack api calls to a specififc file
MarcosSpessatto Apr 10, 2019
aeafd79
Changed all references directly to the slack api
MarcosSpessatto Apr 12, 2019
7b63b36
Fix reactions to execute unsetReaction always when a reaction was rem…
MarcosSpessatto Apr 12, 2019
df8d7e2
Add function to retrieve all members of slack channel and remove old …
MarcosSpessatto Apr 15, 2019
ae51bf0
Add function to retrieve all members of slack channel and remove old …
MarcosSpessatto Apr 15, 2019
b5ddad5
Merge remote-tracking branch 'nylen/fix/slackbridge-private-channels'…
MarcosSpessatto Apr 15, 2019
d1c8561
Fix send files from slack to rocket
MarcosSpessatto Apr 15, 2019
71f3ca8
Fix lint and remove unused code
MarcosSpessatto Apr 15, 2019
7b1eddd
Merge branch 'develop' into fix/slackbridge-private-channels
MarcosSpessatto Apr 15, 2019
7a91d79
Fix error on slackbridge importer
MarcosSpessatto Apr 15, 2019
73bf91e
Fix roompick in admin page, related to choose room to publish in slac…
MarcosSpessatto Apr 15, 2019
480e93e
Merge branch 'develop' into fix/slackbridge-private-channels
sampaiodiego Apr 18, 2019
288ad55
Fix callbacks order
MarcosSpessatto Apr 18, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
118 changes: 60 additions & 58 deletions app/slackbridge/server/RocketAdapter.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
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';
import { SlackAPI } from './SlackAPI';

export default class RocketAdapter {
constructor(slackBridge) {
Expand All @@ -17,6 +17,11 @@ export default class RocketAdapter {
this.util = util;
this.userTags = {};
this.slack = {};
this.slackAPI = {};
}

setSlackAPI(apiToken) {
this.slackAPI = new SlackAPI(apiToken);
}

connect() {
Expand Down Expand Up @@ -49,7 +54,7 @@ export default class RocketAdapter {

onMessageDelete(rocketMessageDeleted) {
try {
if (! this.slack.getSlackChannel(rocketMessageDeleted.rid)) {
if (!this.slack.getSlackChannel(rocketMessageDeleted.rid)) {
// This is on a channel that the rocket bot is not subscribed
return;
}
Expand Down Expand Up @@ -116,7 +121,7 @@ export default class RocketAdapter {

onMessage(rocketMessage) {
try {
if (! this.slack.getSlackChannel(rocketMessage.rid)) {
if (!this.slack.getSlackChannel(rocketMessage.rid)) {
// This is on a channel that the rocket bot is not subscribed
return;
}
Expand Down Expand Up @@ -173,32 +178,24 @@ 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')) {
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);
Expand Down Expand Up @@ -235,43 +232,50 @@ 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 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);
const slackChannel = this.slackAPI.getRoomInfo(slackChannelID);
if (slackChannel) {
const members = this.slackAPI.getMembers(slackChannelID);
if (!members) {
logger.rocket.error('Could not fetch room members');
return;
}
const rocketRoom = Rooms.findOneByName(slackChannel.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);
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, rocketUserCreator.username, rocketUsers);
slackChannel.rocketId = rocketChannel.rid;
} catch (e) {
if (!hasRetried) {
logger.rocket.debug('Error adding channel from Slack. Will retry in 1s.', e.message);
Expand All @@ -284,23 +288,22 @@ 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);
this.slack.addSlackChannel(rocketChannelData.rocketId, slackChannelID);
Rooms.addImportIds(slackChannel.rocketId, slackChannel.id);
}
return Rooms.findOneById(rocketChannelData.rocketId);
this.slack.addSlackChannel(slackChannel.rocketId, slackChannelID);
return Rooms.findOneById(slackChannel.rocketId);
}
logger.rocket.debug('Channel not added');
return;
}

findUser(slackUserID) {
Expand All @@ -313,9 +316,9 @@ 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 user = this.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 @@ -389,7 +392,6 @@ export default class RocketAdapter {
return Users.findOneById(rocketUserData.rocketId);
}
logger.rocket.debug('User not added');
return;
}

addAliasToMsg(rocketUserName, rocketMsgObj) {
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