You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
320 lines
14 KiB
320 lines
14 KiB
Importer.Slack = class Importer.Slack extends Importer.Base
|
|
constructor: (name, descriptionI18N, fileTypeRegex) ->
|
|
super(name, descriptionI18N, fileTypeRegex)
|
|
@userTags = []
|
|
@bots = {}
|
|
|
|
prepare: (dataURI, sentContentType, fileName) =>
|
|
super(dataURI, sentContentType, fileName)
|
|
|
|
# try
|
|
{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 == 'channels.json'
|
|
@updateProgress Importer.ProgressStep.PREPARING_CHANNELS
|
|
tempChannels = JSON.parse entry.getData().toString()
|
|
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
|
|
console.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
|
|
@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
|
|
|
|
@updateProgress Importer.ProgressStep.USER_SELECTION
|
|
return new Importer.Selection @name, selectionUsers, selectionChannels
|
|
# catch error
|
|
# @updateRecord { 'failed': true, 'error': error }
|
|
# console.error Importer.ProgressStep.ERROR
|
|
# throw new Error 'import-slack-error', error
|
|
|
|
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 =>
|
|
# try
|
|
@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 existantUser
|
|
user.rocketId = existantUser._id
|
|
@userTags.push
|
|
slack: "<@#{user.id}>"
|
|
slackLong: "<@#{user.id}|#{user.name}>"
|
|
rocket: "@#{existantUser.username}"
|
|
else
|
|
userId = Accounts.createUser { email: user.profile.email, password: Date.now() + user.name + user.profile.email.toUpperCase() }
|
|
Meteor.runAsUser userId, () =>
|
|
Meteor.call 'setUsername', user.name
|
|
Meteor.call 'joinDefaultChannels', true
|
|
url = null
|
|
if user.profile.image_original
|
|
url = user.profile.image_original
|
|
else if user.profile.image_512
|
|
url = user.profile.image_512
|
|
Meteor.call 'setAvatarFromService', url, null, 'url'
|
|
# Slack's is -18000 which translates to Rocket.Chat's after dividing by 3600
|
|
if user.tz_offset
|
|
Meteor.call 'updateUserUtcOffset', user.tz_offset / 3600
|
|
|
|
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
|
|
else
|
|
users = []
|
|
for member in channel.members when member isnt channel.creator
|
|
user = @getRocketUser member
|
|
if user?
|
|
users.push user.username
|
|
|
|
userId = ''
|
|
for user in @users.users when user.id is channel.creator
|
|
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
|
|
lastSetTopic = channel.topic.last_set
|
|
|
|
if not _.isEmpty(channel.purpose?.value) and channel.purpose.last_set > lastSetTopic
|
|
roomUpdate.topic = channel.purpose.value
|
|
|
|
RocketChat.models.Rooms.update { _id: channel.rocketId }, { $set: roomUpdate }
|
|
|
|
@addCountCompleted 1
|
|
@collection.update { _id: @channels._id }, { $set: { 'channels': @channels.channels }}
|
|
|
|
missedTypes = {}
|
|
ignoreTypes = { 'bot_add': true, 'file_comment': true, 'file_mention': true, 'channel_name': 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
|
|
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), { ts: new Date(parseInt(message.ts.split('.')[0]) * 1000) }
|
|
else if message.subtype is 'channel_leave'
|
|
if @getRocketUser(message.user)?
|
|
RocketChat.models.Messages.createUserLeaveWithRoomIdAndUser room._id, @getRocketUser(message.user), { ts: new Date(parseInt(message.ts.split('.')[0]) * 1000) }
|
|
else if message.subtype is 'me_message'
|
|
RocketChat.sendMessage @getRocketUser(message.user), { msg: '_' + @convertSlackMessageToRocketChat(message.text) + '_', ts: new Date(parseInt(message.ts.split('.')[0]) * 1000) }, room
|
|
else if message.subtype is 'bot_message'
|
|
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: if message.text then message.text else ''
|
|
ts: new Date(parseInt(message.ts.split('.')[0]) * 1000)
|
|
rid: room._id
|
|
bot: true
|
|
attachments: message.attachments
|
|
username: if botUsername then botUsername else undefined
|
|
|
|
if message.edited?
|
|
msgObj.ets = new Date(parseInt(message.edited.ts.split('.')[0]) * 1000)
|
|
|
|
if message.icons?
|
|
msgObj.emoji = message.icons.emoji
|
|
|
|
msgObj.msg = @convertSlackMessageToRocketChat(msgObj.msg)
|
|
|
|
RocketChat.sendMessage botUser, msgObj, room
|
|
else if message.subtype is 'channel_purpose'
|
|
RocketChat.models.Messages.createRoomSettingsChangedWithTypeRoomIdMessageAndUser 'room_changed_topic', room._id, message.purpose, @getRocketUser(message.user), { ts: new Date(parseInt(message.ts.split('.')[0]) * 1000) }
|
|
else if message.subtype is 'channel_topic'
|
|
RocketChat.models.Messages.createRoomSettingsChangedWithTypeRoomIdMessageAndUser 'room_changed_topic', room._id, message.topic, @getRocketUser(message.user), { ts: new Date(parseInt(message.ts.split('.')[0]) * 1000) }
|
|
else if message.subtype is 'pinned_item'
|
|
RocketChat.models.Messages.createWithTypeRoomIdMessageAndUser 'message_pinned', room._id, '', @getRocketUser(message.user),
|
|
ts: new Date(parseInt(message.ts.split('.')[0]) * 1000)
|
|
attachments: [
|
|
"text" : @convertSlackMessageToRocketChat message.attachments[0].text
|
|
"author_name" : message.attachments[0].author_subname
|
|
"author_icon" : getAvatarUrlFromUsername(message.attachments[0].author_subname)
|
|
]
|
|
else if message.subtype is 'file_share'
|
|
if message.file?.url_private_download isnt undefined
|
|
details =
|
|
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
|
|
ts: new Date(parseInt(message.ts.split('.')[0]) * 1000)
|
|
rid: room._id
|
|
u:
|
|
_id: user._id
|
|
username: user.username
|
|
|
|
if message.edited?
|
|
msgObj.ets = new Date(parseInt(message.edited.ts.split('.')[0]) * 1000)
|
|
|
|
RocketChat.sendMessage @getRocketUser(message.user), msgObj, room
|
|
@addCountCompleted 1
|
|
console.log 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
|
|
console.log "Import took #{timeTook} milliseconds."
|
|
# catch error
|
|
# @updateRecord { 'failed': true, 'error': error }
|
|
# @updateProgress Importer.ProgressStep.ERROR
|
|
# console.error Importer.ProgressStep.ERROR
|
|
# throw new Error 'import-slack-error', error
|
|
|
|
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 }}
|
|
|
|
convertSlackMessageToRocketChat: (message) =>
|
|
if message?
|
|
message = message.replace /<!everyone>/g, '@all'
|
|
message = message.replace /<!channel>/g, '@all'
|
|
message = message.replace />/g, '<'
|
|
message = message.replace /</g, '>'
|
|
message = message.replace /&/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
|
|
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
|
|
|
|
return new Importer.Selection @name, selectionUsers, selectionChannels
|
|
|