pull/6987/head
Guilherme Gazzo 9 years ago
parent ec0683dd0c
commit ea872897c1
  1. 5
      .eslintrc
  2. 3
      packages/rocketchat-importer-slack/main.coffee
  3. 5
      packages/rocketchat-importer-slack/main.js
  4. 5
      packages/rocketchat-importer-slack/package.js
  5. 373
      packages/rocketchat-importer-slack/server.coffee
  6. 434
      packages/rocketchat-importer-slack/server.js

@ -1,7 +1,10 @@
{
"parserOptions": {
"sourceType": "module",
"ecmaVersion": 2017
"ecmaVersion": 2017,
"ecmaFeatures": {
"experimentalObjectRestSpread" : true,
}
},
"env": {
"browser": true,

@ -1,3 +0,0 @@
Importer.addImporter 'slack', Importer.Slack,
name: 'Slack'
mimeType: 'application/zip'

@ -0,0 +1,5 @@
/* globals Importer */
Importer.addImporter('slack', Importer.Slack, {
name: 'Slack',
mimeType: 'application/zip'
});

@ -8,11 +8,10 @@ Package.describe({
Package.onUse(function(api) {
api.use([
'ecmascript',
'coffeescript',
'rocketchat:lib',
'rocketchat:importer'
]);
api.use('rocketchat:logger', 'server');
api.addFiles('server.coffee', 'server');
api.addFiles('main.coffee', ['client', 'server']);
api.addFiles('server.js', 'server');
api.addFiles('main.js', ['client', 'server']);
});

@ -1,373 +0,0 @@
Importer.Slack = class Importer.Slack extends Importer.Base
constructor: (name, descriptionI18N, mimeType) ->
super(name, descriptionI18N, mimeType)
@userTags = []
@bots = {}
@logger.debug('Constructed a new Slack Importer.')
prepare: (dataURI, sentContentType, fileName) =>
super(dataURI, sentContentType, fileName)
{image, contentType} = RocketChatFile.dataURIParse dataURI
zip = new @AdmZip(new Buffer(image, 'base64'))
zipEntries = zip.getEntries()
tempChannels = []
tempUsers = []
tempMessages = {}
for entry in zipEntries
do (entry) =>
if entry.entryName.indexOf('__MACOSX') > -1
#ignore all of the files inside of __MACOSX
@logger.debug("Ignoring the file: #{entry.entryName}")
else if entry.entryName == 'channels.json'
@updateProgress Importer.ProgressStep.PREPARING_CHANNELS
tempChannels = JSON.parse entry.getData().toString()
tempChannels = tempChannels.filter (channel) -> channel.creator?
else if entry.entryName == 'users.json'
@updateProgress Importer.ProgressStep.PREPARING_USERS
tempUsers = JSON.parse entry.getData().toString()
for user in tempUsers when user.is_bot
@bots[user.profile.bot_id] = user
else if not entry.isDirectory and entry.entryName.indexOf('/') > -1
item = entry.entryName.split('/') #random/2015-10-04.json
channelName = item[0] #random
msgGroupData = item[1].split('.')[0] #2015-10-04
if not tempMessages[channelName]
tempMessages[channelName] = {}
# Catch files which aren't valid JSON files, ignore them
try
tempMessages[channelName][msgGroupData] = JSON.parse entry.getData().toString()
catch
@logger.warn "#{entry.entryName} is not a valid JSON file! Unable to import it."
# Insert the users record, eventually this might have to be split into several ones as well
# if someone tries to import a several thousands users instance
usersId = @collection.insert { 'import': @importRecord._id, 'importer': @name, 'type': 'users', 'users': tempUsers }
@users = @collection.findOne usersId
@updateRecord { 'count.users': tempUsers.length }
@addCountToTotal tempUsers.length
# Insert the channels records.
channelsId = @collection.insert { 'import': @importRecord._id, 'importer': @name, 'type': 'channels', 'channels': tempChannels }
@channels = @collection.findOne channelsId
@updateRecord { 'count.channels': tempChannels.length }
@addCountToTotal tempChannels.length
# Insert the messages records
@updateProgress Importer.ProgressStep.PREPARING_MESSAGES
messagesCount = 0
for channel, messagesObj of tempMessages
do (channel, messagesObj) =>
if not @messages[channel]
@messages[channel] = {}
for date, msgs of messagesObj
messagesCount += msgs.length
@updateRecord { 'messagesstatus': "#{channel}/#{date}" }
if Importer.Base.getBSONSize(msgs) > Importer.Base.MaxBSONSize
for splitMsg, i in Importer.Base.getBSONSafeArraysFromAnArray(msgs)
messagesId = @collection.insert { 'import': @importRecord._id, 'importer': @name, 'type': 'messages', 'name': "#{channel}/#{date}.#{i}", 'messages': splitMsg }
@messages[channel]["#{date}.#{i}"] = @collection.findOne messagesId
else
messagesId = @collection.insert { 'import': @importRecord._id, 'importer': @name, 'type': 'messages', 'name': "#{channel}/#{date}", 'messages': msgs }
@messages[channel][date] = @collection.findOne messagesId
@updateRecord { 'count.messages': messagesCount, 'messagesstatus': null }
@addCountToTotal messagesCount
if tempUsers.length is 0 or tempChannels.length is 0 or messagesCount is 0
@logger.warn "The loaded users count #{tempUsers.length}, the loaded channels #{tempChannels.length}, and the loaded messages #{messagesCount}"
@updateProgress Importer.ProgressStep.ERROR
return @getProgress()
selectionUsers = tempUsers.map (user) ->
return new Importer.SelectionUser user.id, user.name, user.profile.email, user.deleted, user.is_bot, !user.is_bot
selectionChannels = tempChannels.map (channel) ->
return new Importer.SelectionChannel channel.id, channel.name, channel.is_archived, true, false
@updateProgress Importer.ProgressStep.USER_SELECTION
return new Importer.Selection @name, selectionUsers, selectionChannels
startImport: (importSelection) =>
super(importSelection)
start = Date.now()
for user in importSelection.users
for u in @users.users when u.id is user.user_id
u.do_import = user.do_import
@collection.update { _id: @users._id }, { $set: { 'users': @users.users }}
for channel in importSelection.channels
for c in @channels.channels when c.id is channel.channel_id
c.do_import = channel.do_import
@collection.update { _id: @channels._id }, { $set: { 'channels': @channels.channels }}
startedByUserId = Meteor.userId()
Meteor.defer =>
@updateProgress Importer.ProgressStep.IMPORTING_USERS
for user in @users.users when user.do_import
do (user) =>
Meteor.runAsUser startedByUserId, () =>
existantUser = RocketChat.models.Users.findOneByEmailAddress user.profile.email
if not existantUser
existantUser = RocketChat.models.Users.findOneByUsername user.name
if existantUser
user.rocketId = existantUser._id
RocketChat.models.Users.update { _id: user.rocketId }, { $addToSet: { importIds: user.id } }
@userTags.push
slack: "<@#{user.id}>"
slackLong: "<@#{user.id}|#{user.name}>"
rocket: "@#{existantUser.username}"
else
if user.profile.email
userId = Accounts.createUser { email: user.profile.email, password: Date.now() + user.name + user.profile.email.toUpperCase() }
else
userId = Accounts.createUser { username: user.name, password: Date.now() + user.name, joinDefaultChannelsSilenced: true }
Meteor.runAsUser userId, () =>
Meteor.call 'setUsername', user.name, {joinDefaultChannelsSilenced: true}
url = null
if user.profile.image_original
url = user.profile.image_original
else if user.profile.image_512
url = user.profile.image_512
try
Meteor.call 'setAvatarFromService', url, undefined, 'url'
catch error
this.logger.warn "Failed to set #{user.name}'s avatar from url #{url}"
# Slack's is -18000 which translates to Rocket.Chat's after dividing by 3600
if user.tz_offset
Meteor.call 'userSetUtcOffset', user.tz_offset / 3600
RocketChat.models.Users.update { _id: userId }, { $addToSet: { importIds: user.id } }
if user.profile.real_name
RocketChat.models.Users.setName userId, user.profile.real_name
#Deleted users are 'inactive' users in Rocket.Chat
if user.deleted
Meteor.call 'setUserActiveStatus', userId, false
#TODO: Maybe send emails?
user.rocketId = userId
@userTags.push
slack: "<@#{user.id}>"
slackLong: "<@#{user.id}|#{user.name}>"
rocket: "@#{user.name}"
@addCountCompleted 1
@collection.update { _id: @users._id }, { $set: { 'users': @users.users }}
@updateProgress Importer.ProgressStep.IMPORTING_CHANNELS
for channel in @channels.channels when channel.do_import
do (channel) =>
Meteor.runAsUser startedByUserId, () =>
existantRoom = RocketChat.models.Rooms.findOneByName channel.name
if existantRoom or channel.is_general
if channel.is_general and channel.name isnt existantRoom?.name
Meteor.call 'saveRoomSettings', 'GENERAL', 'roomName', channel.name
channel.rocketId = if channel.is_general then 'GENERAL' else existantRoom._id
RocketChat.models.Rooms.update { _id: channel.rocketId }, { $addToSet: { importIds: channel.id } }
else
users = []
for member in channel.members when member isnt channel.creator
user = @getRocketUser member
if user?
users.push user.username
userId = startedByUserId
for user in @users.users when user.id is channel.creator and user.do_import
userId = user.rocketId
Meteor.runAsUser userId, () =>
returned = Meteor.call 'createChannel', channel.name, users
channel.rocketId = returned.rid
# @TODO implement model specific function
roomUpdate =
ts: new Date(channel.created * 1000)
if not _.isEmpty channel.topic?.value
roomUpdate.topic = channel.topic.value
if not _.isEmpty(channel.purpose?.value)
roomUpdate.description = channel.purpose.value
RocketChat.models.Rooms.update { _id: channel.rocketId }, { $set: roomUpdate, $addToSet: { importIds: channel.id } }
@addCountCompleted 1
@collection.update { _id: @channels._id }, { $set: { 'channels': @channels.channels }}
missedTypes = {}
ignoreTypes = { 'bot_add': true, 'file_comment': true, 'file_mention': true }
@updateProgress Importer.ProgressStep.IMPORTING_MESSAGES
for channel, messagesObj of @messages
do (channel, messagesObj) =>
Meteor.runAsUser startedByUserId, () =>
slackChannel = @getSlackChannelFromName channel
if slackChannel?.do_import
room = RocketChat.models.Rooms.findOneById slackChannel.rocketId, { fields: { usernames: 1, t: 1, name: 1 } }
for date, msgs of messagesObj
@updateRecord { 'messagesstatus': "#{channel}/#{date}.#{msgs.messages.length}" }
for message in msgs.messages
msgDataDefaults =
_id: "slack-#{slackChannel.id}-#{message.ts.replace(/\./g, '-')}"
ts: new Date(parseInt(message.ts.split('.')[0]) * 1000)
if message.type is 'message'
if message.subtype?
if message.subtype is 'channel_join'
if @getRocketUser(message.user)?
RocketChat.models.Messages.createUserJoinWithRoomIdAndUser room._id, @getRocketUser(message.user), msgDataDefaults
else if message.subtype is 'channel_leave'
if @getRocketUser(message.user)?
RocketChat.models.Messages.createUserLeaveWithRoomIdAndUser room._id, @getRocketUser(message.user), msgDataDefaults
else if message.subtype is 'me_message'
msgObj =
msg: "_#{@convertSlackMessageToRocketChat(message.text)}_"
_.extend msgObj, msgDataDefaults
RocketChat.sendMessage @getRocketUser(message.user), msgObj, room, true
else if message.subtype is 'bot_message' or message.subtype is 'slackbot_response'
botUser = RocketChat.models.Users.findOneById 'rocket.cat', { fields: { username: 1 }}
botUsername = if @bots[message.bot_id] then @bots[message.bot_id]?.name else message.username
msgObj =
msg: @convertSlackMessageToRocketChat(message.text)
rid: room._id
bot: true
attachments: message.attachments
username: if botUsername then botUsername else undefined
_.extend msgObj, msgDataDefaults
if message.edited?
msgObj.editedAt = new Date(parseInt(message.edited.ts.split('.')[0]) * 1000)
editedBy = @getRocketUser(message.edited.user)
if editedBy?
msgObj.editedBy =
_id: editedBy._id
username: editedBy.username
if message.icons?
msgObj.emoji = message.icons.emoji
RocketChat.sendMessage botUser, msgObj, room, true
else if message.subtype is 'channel_purpose'
if @getRocketUser(message.user)?
RocketChat.models.Messages.createRoomSettingsChangedWithTypeRoomIdMessageAndUser 'room_changed_description', room._id, message.purpose, @getRocketUser(message.user), msgDataDefaults
else if message.subtype is 'channel_topic'
if @getRocketUser(message.user)?
RocketChat.models.Messages.createRoomSettingsChangedWithTypeRoomIdMessageAndUser 'room_changed_topic', room._id, message.topic, @getRocketUser(message.user), msgDataDefaults
else if message.subtype is 'channel_name'
if @getRocketUser(message.user)?
RocketChat.models.Messages.createRoomRenamedWithRoomIdRoomNameAndUser room._id, message.name, @getRocketUser(message.user), msgDataDefaults
else if message.subtype is 'pinned_item'
if message.attachments
msgObj =
attachments: [
"text" : @convertSlackMessageToRocketChat message.attachments[0].text
"author_name" : message.attachments[0].author_subname
"author_icon" : getAvatarUrlFromUsername(message.attachments[0].author_subname)
]
_.extend msgObj, msgDataDefaults
RocketChat.models.Messages.createWithTypeRoomIdMessageAndUser 'message_pinned', room._id, '', @getRocketUser(message.user), msgObj
else
#TODO: make this better
@logger.debug('Pinned item with no attachment, needs work.');
#RocketChat.models.Messages.createWithTypeRoomIdMessageAndUser 'message_pinned', room._id, '', @getRocketUser(message.user), msgDataDefaults
else if message.subtype is 'file_share'
if message.file?.url_private_download isnt undefined
details =
message_id: "slack-#{message.ts.replace(/\./g, '-')}"
name: message.file.name
size: message.file.size
type: message.file.mimetype
rid: room._id
@uploadFile details, message.file.url_private_download, @getRocketUser(message.user), room, new Date(parseInt(message.ts.split('.')[0]) * 1000)
else
if not missedTypes[message.subtype] and not ignoreTypes[message.subtype]
missedTypes[message.subtype] = message
else
user = @getRocketUser(message.user)
if user?
msgObj =
msg: @convertSlackMessageToRocketChat message.text
rid: room._id
u:
_id: user._id
username: user.username
_.extend msgObj, msgDataDefaults
if message.edited?
msgObj.editedAt = new Date(parseInt(message.edited.ts.split('.')[0]) * 1000)
editedBy = @getRocketUser(message.edited.user)
if editedBy?
msgObj.editedBy =
_id: editedBy._id
username: editedBy.username
RocketChat.sendMessage @getRocketUser(message.user), msgObj, room, true
# Process the reactions
if RocketChat.models.Messages.findOneById(msgDataDefaults._id)? and message.reactions?.length > 0
for reaction in message.reactions
for u in reaction.users
rcUser = @getRocketUser(u)
if rcUser?
Meteor.runAsUser rcUser._id, () =>
Meteor.call 'setReaction', ":#{reaction.name}:", msgDataDefaults._id
@addCountCompleted 1
if not _.isEmpty missedTypes
console.log 'Missed import types:', missedTypes
@updateProgress Importer.ProgressStep.FINISHING
for channel in @channels.channels when channel.do_import and channel.is_archived
do (channel) =>
Meteor.runAsUser startedByUserId, () =>
Meteor.call 'archiveRoom', channel.rocketId
@updateProgress Importer.ProgressStep.DONE
timeTook = Date.now() - start
@logger.log "Import took #{timeTook} milliseconds."
return @getProgress()
getSlackChannelFromName: (channelName) =>
for channel in @channels.channels when channel.name is channelName
return channel
getRocketUser: (slackId) =>
for user in @users.users when user.id is slackId
return RocketChat.models.Users.findOneById user.rocketId, { fields: { username: 1, name: 1 }}
convertSlackMessageToRocketChat: (message) =>
if message?
message = message.replace /<!everyone>/g, '@all'
message = message.replace /<!channel>/g, '@all'
message = message.replace /<!here>/g, '@here'
message = message.replace /&gt;/g, '>'
message = message.replace /&lt;/g, '<'
message = message.replace /&amp;/g, '&'
message = message.replace /:simple_smile:/g, ':smile:'
message = message.replace /:memo:/g, ':pencil:'
message = message.replace /:piggy:/g, ':pig:'
message = message.replace /:uk:/g, ':gb:'
message = message.replace /<(http[s]?:[^>]*)>/g, '$1'
for userReplace in @userTags
message = message.replace userReplace.slack, userReplace.rocket
message = message.replace userReplace.slackLong, userReplace.rocket
else
message = ''
return message
getSelection: () =>
selectionUsers = @users.users.map (user) ->
return new Importer.SelectionUser user.id, user.name, user.profile.email, user.deleted, user.is_bot, !user.is_bot
selectionChannels = @channels.channels.map (channel) ->
return new Importer.SelectionChannel channel.id, channel.name, channel.is_archived, true, false
return new Importer.Selection @name, selectionUsers, selectionChannels

@ -0,0 +1,434 @@
/* globals Importer */
Importer.Slack = class extends Importer.Base {
constructor(name, descriptionI18N, mimeType) {
super(name, descriptionI18N, mimeType);
this.userTags = [];
this.bots = {};
this.logger.debug('Constructed a new Slack Importer.');
}
prepare(dataURI, sentContentType, fileName) {
super.prepare(dataURI, sentContentType, fileName);
const {image/*, contentType*/} = RocketChatFile.dataURIParse(dataURI);
const zip = new this.AdmZip(new Buffer(image, 'base64'));
const zipEntries = zip.getEntries();
let tempChannels = [];
let tempUsers = [];
const tempMessages = {};
zipEntries.forEach(entry => {
if (entry.entryName.indexOf('__MACOSX') > -1) {
return this.logger.debug(`Ignoring the file: ${ entry.entryName }`);
}
if (entry.entryName === 'channels.json') {
this.updateProgress(Importer.ProgressStep.PREPARING_CHANNELS);
tempChannels = JSON.parse(entry.getData().toString()).filter(channel => channel.creator != null);
return;
}
if (entry.entryName === 'users.json') {
this.updateProgress(Importer.ProgressStep.PREPARING_USERS);
tempUsers = JSON.parse(entry.getData().toString());
return tempUsers.forEach(user => {
if (user.is_bot) {
this.bots[user.profile.bot_id] = user;
}
});
}
if (!entry.isDirectory && entry.entryName.indexOf('/') > -1) {
const item = entry.entryName.split('/');
const channelName = item[0];
const msgGroupData = item[1].split('.')[0];
tempMessages[channelName] = tempMessages[channelName] || {};
try {
tempMessages[channelName][msgGroupData] = JSON.parse(entry.getData().toString());
} catch (error) {
this.logger.warn(`${ entry.entryName } is not a valid JSON file! Unable to import it.`);
}
}
});
// Insert the users record, eventually this might have to be split into several ones as well
// if someone tries to import a several thousands users instance
const usersId = this.collection.insert({ 'import': this.importRecord._id, 'importer': this.name, 'type': 'users', 'users': tempUsers });
this.users = this.collection.findOne(usersId);
this.updateRecord({ 'count.users': tempUsers.length });
this.addCountToTotal(tempUsers.length);
// Insert the channels records.
const channelsId = this.collection.insert({ 'import': this.importRecord._id, 'importer': this.name, 'type': 'channels', 'channels': tempChannels });
this.channels = this.collection.findOne(channelsId);
this.updateRecord({ 'count.channels': tempChannels.length });
this.addCountToTotal(tempChannels.length);
// Insert the messages records
this.updateProgress(Importer.ProgressStep.PREPARING_MESSAGES);
let messagesCount = 0;
Object.keys(tempMessages).forEach(channel => {
const messagesObj = tempMessages[channel];
this.messages[channel] = this.messages[channel] || {};
Object.keys(messagesObj).forEach(date => {
const msgs = messagesObj[date];
messagesCount += msgs.length;
this.updateRecord({ 'messagesstatus': '#{channel}/#{date}' });
if (Importer.Base.getBSONSize(msgs) > Importer.Base.MaxBSONSize) {
const tmp = Importer.Base.getBSONSafeArraysFromAnArray(msgs);
Object.keys(tmp).forEach(i => {
const splitMsg = tmp[i];
const messagesId = this.collection.insert({ 'import': this.importRecord._id, 'importer': this.name, 'type': 'messages', 'name': `${ channel }/${ date }.${ i }`, 'messages': splitMsg });
this.messages[channel][`${ date }.${ i }`] = this.collection.findOne(messagesId);
});
} else {
const messagesId = this.collection.insert({ 'import': this.importRecord._id, 'importer': this.name, 'type': 'messages', 'name': `${ channel }/${ date }`, 'messages': msgs });
this.messages[channel][date] = this.collection.findOne(messagesId);
}
});
});
this.updateRecord({ 'count.messages': messagesCount, 'messagesstatus': null });
this.addCountToTotal(messagesCount);
if ([tempUsers.length, tempChannels.length, messagesCount].some(e => e === 0)) {
this.logger.warn(`The loaded users count ${ tempUsers.length }, the loaded channels ${ tempChannels.length }, and the loaded messages ${ messagesCount }`);
console.log(`The loaded users count ${ tempUsers.length }, the loaded channels ${ tempChannels.length }, and the loaded messages ${ messagesCount }`);
this.updateProgress(Importer.ProgressStep.ERROR);
return this.getProgress();
}
const selectionUsers = tempUsers.map(user => new Importer.SelectionUser(user.id, user.name, user.profile.email, user.deleted, user.is_bot, !user.is_bot));
const selectionChannels = tempChannels.map(channel => new Importer.SelectionChannel(channel.id, channel.name, channel.is_archived, true, false));
this.updateProgress(Importer.ProgressStep.USER_SELECTION);
return new Importer.Selection(this.name, selectionUsers, selectionChannels);
}
startImport(importSelection) {
super.startImport(importSelection);
const start = Date.now();
Object.keys(importSelection.users).forEach(key => {
const user = importSelection.users[key];
Object.keys(this.users.users).forEach(k => {
const u = this.users.users[k];
if (u.id === user.user_id) {
u.do_import = user.do_import;
}
});
});
this.collection.update({ _id: this.users._id }, { $set: { 'users': this.users.users }});
Object.keys(importSelection.channels).forEach(key => {
const channel = importSelection.channels[key];
Object.keys(this.channels.channels).forEach(k => {
const c = this.channels.channels[k];
if (c.id === channel.channel_id) {
c.do_import = channel.do_import;
}
});
});
this.collection.update({ _id: this.channels._id }, { $set: { 'channels': this.channels.channels }});
const startedByUserId = Meteor.userId();
Meteor.defer(() => {
this.updateProgress(Importer.ProgressStep.IMPORTING_USERS);
this.users.users.forEach(user => {
if (!user.do_import) {
return;
}
Meteor.runAsUser(startedByUserId, () => {
const existantUser = RocketChat.models.Users.findOneByEmailAddress(user.profile.email) || RocketChat.models.Users.findOneByUsername(user.name);
if (existantUser) {
user.rocketId = existantUser._id;
RocketChat.models.Users.update({ _id: user.rocketId }, { $addToSet: { importIds: user.id } });
this.userTags.push({
slack: `<@${ user.id }>`,
slackLong: `<@${ user.id }|${ user.name }>`,
rocket: `@${ existantUser.username }`
});
} else {
const userId = user.profile.email ? Accounts.createUser({ email: user.profile.email, password: Date.now() + user.name + user.profile.email.toUpperCase() }) : Accounts.createUser({ username: user.name, password: Date.now() + user.name, joinDefaultChannelsSilenced: true });
Meteor.runAsUser(userId, () => {
Meteor.call('setUsername', user.name, {joinDefaultChannelsSilenced: true});
const url = user.profile.image_original || user.profile.image_512;
try {
Meteor.call('setAvatarFromService', url, undefined, 'url');
} catch (error) {
this.logger.warn(`Failed to set ${ user.name }'s avatar from url ${ url }`);
console.log(`Failed to set ${ user.name }'s avatar from url ${ url }`);
}
// Slack's is -18000 which translates to Rocket.Chat's after dividing by 3600
if (user.tz_offset) {
Meteor.call('userSetUtcOffset', user.tz_offset / 3600);
}
});
RocketChat.models.Users.update({ _id: userId }, { $addToSet: { importIds: user.id } });
if (user.profile.real_name) {
RocketChat.models.Users.setName(userId, user.profile.real_name);
}
//Deleted users are 'inactive' users in Rocket.Chat
if (user.deleted) {
Meteor.call('setUserActiveStatus', userId, false);
}
//TODO: Maybe send emails?
user.rocketId = userId;
this.userTags.push({
slack: `<@${ user.id }>`,
slackLong: `<@${ user.id }|${ user.name }>`,
rocket: `@${ user.name }`
});
}
this.addCountCompleted(1);
});
});
this.collection.update({ _id: this.users._id }, { $set: { 'users': this.users.users }});
this.updateProgress(Importer.ProgressStep.IMPORTING_CHANNELS);
this.channels.channels.forEach(channel => {
if (!channel.do_import) {
return;
}
Meteor.runAsUser (startedByUserId, () => {
const existantRoom = RocketChat.models.Rooms.findOneByName(channel.name);
if (existantRoom || channel.is_general) {
if (channel.is_general && existantRoom && channel.name !== existantRoom.name) {
Meteor.call('saveRoomSettings', 'GENERAL', 'roomName', channel.name);
}
channel.rocketId = channel.is_general ? 'GENERAL' : existantRoom._id;
RocketChat.models.Rooms.update({ _id: channel.rocketId }, { $addToSet: { importIds: channel.id } });
} else {
const users = channel.members
.reduce((ret, member) => {
if (member !== channel.creator) {
const user = this.getRocketUser(member);
if (user && user.username) {
ret.push(user.username);
}
}
return ret;
}, []);
let userId = startedByUserId;
this.users.users.forEach(user => {
if (user.id === channel.creator && user.do_import) {
userId = user.rocketId;
}
});
Meteor.runAsUser(userId, () => {
const returned = Meteor.call('createChannel', channel.name, users);
channel.rocketId = returned.rid;
});
// @TODO implement model specific function
const roomUpdate = {
ts: new Date(channel.created * 1000)
};
if (!_.isEmpty(channel.topic && channel.topic.value)) {
roomUpdate.topic = channel.topic.value;
}
if (!_.isEmpty(channel.purpose && channel.purpose.value)) {
roomUpdate.description = channel.purpose.value;
}
RocketChat.models.Rooms.update({ _id: channel.rocketId }, { $set: roomUpdate, $addToSet: { importIds: channel.id } });
}
this.addCountCompleted(1);
});
});
this.collection.update({ _id: this.channels._id }, { $set: { 'channels': this.channels.channels }});
const missedTypes = {};
const ignoreTypes = { 'bot_add': true, 'file_comment': true, 'file_mention': true };
this.updateProgress(Importer.ProgressStep.IMPORTING_MESSAGES);
Object.keys(this.messages).forEach(channel => {
const messagesObj = this.messages[channel];
Meteor.runAsUser(startedByUserId, () =>{
const slackChannel = this.getSlackChannelFromName(channel);
if (!slackChannel || !slackChannel.do_import) { return; }
const room = RocketChat.models.Rooms.findOneById(slackChannel.rocketId, { fields: { usernames: 1, t: 1, name: 1 } });
Object.keys(messagesObj).forEach(date => {
const msgs = messagesObj[date];
msgs.messages.forEach(message => {
this.updateRecord({ 'messagesstatus': '#{channel}/#{date}.#{msgs.messages.length}' });
const msgDataDefaults ={
_id: `slack-${ slackChannel.id }-${ message.ts.replace(/\./g, '-') }`,
ts: new Date(parseInt(message.ts.split('.')[0]) * 1000)
};
if (message.type === 'message') {
if (message.subtype) {
if (message.subtype === 'channel_join') {
if (this.getRocketUser(message.user)) {
RocketChat.models.Messages.createUserJoinWithRoomIdAndUser(room._id, this.getRocketUser(message.user), msgDataDefaults);
}
} else if (message.subtype === 'channel_leave') {
if (this.getRocketUser(message.user)) { RocketChat.models.Messages.createUserLeaveWithRoomIdAndUser(room._id, this.getRocketUser(message.user), msgDataDefaults); }
} else if (message.subtype === 'me_message') {
const msgObj = {
...msgDataDefaults,
msg: `_${ this.convertSlackMessageToRocketChat(message.text) }_`
};
RocketChat.sendMessage(this.getRocketUser(message.user), msgObj, room, true);
} else if (message.subtype === 'bot_message' || message.subtype === 'slackbot_response') {
const botUser = RocketChat.models.Users.findOneById('rocket.cat', { fields: { username: 1 }});
const botUsername = this.bots[message.bot_id] ? this.bots[message.bot_id].name : message.username;
const msgObj = {
...msgDataDefaults,
msg: this.convertSlackMessageToRocketChat(message.text),
rid: room._id,
bot: true,
attachments: message.attachments,
username: botUsername || undefined
};
if (message.edited) {
msgObj.editedAt = new Date(parseInt(message.edited.ts.split('.')[0]) * 1000);
const editedBy = this.getRocketUser(message.edited.user);
if (editedBy) {
msgObj.editedBy = {
_id: editedBy._id,
username: editedBy.username
};
}
}
if (message.icons) {
msgObj.emoji = message.icons.emoji;
}
RocketChat.sendMessage(botUser, msgObj, room, true);
} else if (message.subtype === 'channel_purpose') {
if (this.getRocketUser(message.user)) {
RocketChat.models.Messages.createRoomSettingsChangedWithTypeRoomIdMessageAndUser('room_changed_description', room._id, message.purpose, this.getRocketUser(message.user), msgDataDefaults);
}
} else if (message.subtype === 'channel_topic') {
if (this.getRocketUser(message.user)) {
RocketChat.models.Messages.createRoomSettingsChangedWithTypeRoomIdMessageAndUser('room_changed_topic', room._id, message.topic, this.getRocketUser(message.user), msgDataDefaults);
}
} else if (message.subtype === 'channel_name') {
if (this.getRocketUser(message.user)) {
RocketChat.models.Messages.createRoomRenamedWithRoomIdRoomNameAndUser(room._id, message.name, this.getRocketUser(message.user), msgDataDefaults);
}
} else if (message.subtype === 'pinned_item') {
if (message.attachments) {
const msgObj = {
...msgDataDefaults,
attachments: [{
'text': this.convertSlackMessageToRocketChat(message.attachments[0].text),
'author_name' : message.attachments[0].author_subname,
'author_icon' : getAvatarUrlFromUsername(message.attachments[0].author_subname)
}]
};
RocketChat.models.Messages.createWithTypeRoomIdMessageAndUser('message_pinned', room._id, '', this.getRocketUser(message.user), msgObj);
} else {
//TODO: make this better
this.logger.debug('Pinned item with no attachment, needs work.');
//RocketChat.models.Messages.createWithTypeRoomIdMessageAndUser 'message_pinned', room._id, '', @getRocketUser(message.user), msgDataDefaults
}
} else if (message.subtype === 'file_share') {
if (message.file && message.file.url_private_download !== undefined) {
const details = {
message_id: `slack-${ message.ts.replace(/\./g, '-') }`,
name: message.file.name,
size: message.file.size,
type: message.file.mimetype,
rid: room._id
};
this.uploadFile(details, message.file.url_private_download, this.getRocketUser(message.user), room, new Date(parseInt(message.ts.split('.')[0]) * 1000));
}
} else if (!missedTypes[message.subtype] && !ignoreTypes[message.subtype]) {
missedTypes[message.subtype] = message;
}
} else {
const user = this.getRocketUser(message.user);
if (user) {
const msgObj = {
...msgDataDefaults,
msg: this.convertSlackMessageToRocketChat(message.text),
rid: room._id,
u: {
_id: user._id,
username: user.username
}
};
if (message.edited) {
msgObj.editedAt = new Date(parseInt(message.edited.ts.split('.')[0]) * 1000);
const editedBy = this.getRocketUser(message.edited.user);
if (editedBy) {
msgObj.editedBy = {
_id: editedBy._id,
username: editedBy.username
};
}
}
RocketChat.sendMessage(this.getRocketUser(message.user), msgObj, room, true);
}
}
}
// Process the reactions
if (RocketChat.models.Messages.findOneById(msgDataDefaults._id) && message.reactions && message.reactions.length > 0) {
message.reactions.forEach(reaction => {
reaction.users.forEach(u => {
const rcUser = this.getRocketUser(u);
if (!rcUser) { return; }
Meteor.runAsUser(rcUser._id, () => Meteor.call('setReaction', `:${ reaction.name }:`, msgDataDefaults._id));
});
});
}
this.addCountCompleted(1);
});
});
});
});
if (!_.isEmpty(missedTypes)) {
console.log('Missed import types:', missedTypes);
}
this.updateProgress(Importer.ProgressStep.FINISHING);
this.channels.channels.forEach(channel => {
if (channel.do_import && channel.is_archived) {
Meteor.runAsUser(startedByUserId, function() {
Meteor.call('archiveRoom', channel.rocketId);
});
}
});
this.updateProgress(Importer.ProgressStep.DONE);
const timeTook = Date.now() - start;
this.logger.log(`Import took ${ timeTook } milliseconds.`);
});
return this.getProgress();
}
getSlackChannelFromName(channelName) {
return this.channels.channels.find(channel => channel.name === channelName);
}
getRocketUser(slackId) {
const user = this.users.users.find(user => user.id === slackId);
if (user) {
return RocketChat.models.Users.findOneById(user.rocketId, { fields: { username: 1, name: 1 }});
}
}
convertSlackMessageToRocketChat(message) {
if (message != null) {
message = message.replace(/<!everyone>/g, '@all');
message = message.replace(/<!channel>/g, '@all');
message = message.replace(/<!here>/g, '@here');
message = message.replace(/&gt;/g, '>');
message = message.replace(/&lt;/g, '<');
message = message.replace(/&amp;/g, '&');
message = message.replace(/:simple_smile:/g, ':smile:');
message = message.replace(/:memo:/g, ':pencil:');
message = message.replace(/:piggy:/g, ':pig:');
message = message.replace(/:uk:/g, ':gb:');
message = message.replace(/<(http[s]?:[^>]*)>/g, '$1');
for (const userReplace of Array.from(this.userTags)) {
message = message.replace(userReplace.slack, userReplace.rocket);
message = message.replace(userReplace.slackLong, userReplace.rocket);
}
} else {
message = '';
}
return message;
}
getSelection() {
const selectionUsers = this.users.users.map(user => new Importer.SelectionUser(user.id, user.name, user.profile.email, user.deleted, user.is_bot, !user.is_bot));
const selectionChannels = this.channels.channels.map(channel => new Importer.SelectionChannel(channel.id, channel.name, channel.is_archived, true, false));
return new Importer.Selection(this.name, selectionUsers, selectionChannels);
}
};
Loading…
Cancel
Save