diff --git a/.eslintignore b/.eslintignore index 03b1ad39e1a6..984ba1d5dc35 100644 --- a/.eslintignore +++ b/.eslintignore @@ -5,7 +5,7 @@ packages/meteor-timesync/ packages/rocketchat-emoji-emojione/generateEmojiIndex.js packages/rocketchat-favico/favico.js packages/rocketchat-katex/client/katex/katex.min.js -packages/rocketchat-livechat/app/node_modules +packages/rocketchat-livechat/.app/node_modules packages/rocketchat-livechat/assets/rocketchat-livechat.min.js packages/rocketchat-livechat/assets/rocket-livechat.js packages/rocketchat-migrations/ diff --git a/.travis.yml b/.travis.yml index 49f5d7d3972d..fb956d945ea3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,8 +32,8 @@ cache: - "$HOME/build/RocketChat/Rocket.Chat/node_modules" - "$HOME/build/RocketChat/Rocket.Chat/.meteor/local" - "$HOME/build/RocketChat/Rocket.Chat/packages/rocketchat-livechat/.npm" - - "$HOME/build/RocketChat/Rocket.Chat/packages/rocketchat-livechat/app/node_modules" - - "$HOME/build/RocketChat/Rocket.Chat/packages/rocketchat-livechat/app/.meteor/local" + - "$HOME/build/RocketChat/Rocket.Chat/packages/rocketchat-livechat/.app/node_modules" + - "$HOME/build/RocketChat/Rocket.Chat/packages/rocketchat-livechat/.app/.meteor/local" before_install: - if [ ! -e "$HOME/.meteor/meteor" ]; then curl https://install.meteor.com | sed s/--progress-bar/-sL/g | /bin/sh; fi # Start X Virtual Frame Buffer for headless testing with real browsers diff --git a/package-lock.json b/package-lock.json index 20b6c455da7d..9cd72042cbdc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7167,7 +7167,7 @@ "postcss-custom-properties": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-6.2.0.tgz", - "integrity": "sha512-eNR2h9T9ciKMoQEORrPjH33XeN/nuvVuxArOKmHtsFbGbNss631tgTrKou3/pmjAZbA4QQkhLIkPQkIk3WW+8w==", + "integrity": "sha1-XZKafwbpuE4PETNBlMC6mjCs++k=", "dev": true, "requires": { "balanced-match": "1.0.0", diff --git a/packages/rocketchat-authorization/server/functions/canAccessRoom.js b/packages/rocketchat-authorization/server/functions/canAccessRoom.js index 82a6b761c1bb..31a6e17674de 100644 --- a/packages/rocketchat-authorization/server/functions/canAccessRoom.js +++ b/packages/rocketchat-authorization/server/functions/canAccessRoom.js @@ -17,9 +17,9 @@ RocketChat.authz.roomAccessValidators = [ } ]; -RocketChat.authz.canAccessRoom = function(room, user) { +RocketChat.authz.canAccessRoom = function(room, user, extraData) { return RocketChat.authz.roomAccessValidators.some((validator) => { - return validator.call(this, room, user); + return validator.call(this, room, user, extraData); }); }; diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index 3738e834d63b..75188fe03472 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -1061,6 +1061,8 @@ "LDAP_Username_Field": "Username Field", "LDAP_Username_Field_Description": "Which field will be used as *username* for new users. Leave empty to use the username informed on login page.
You can use template tags too, like `#{givenName}.#{sn}`.
Default value is `sAMAccountName`.", "Execute_Synchronization_Now": "Execute Synchronization Now", + "Lead_capture_email_regex": "Lead capture email regex", + "Lead_capture_phone_regex": "Lead capture phone regex", "Least_Amount": "Least Amount", "Leave_Group_Warning": "Are you sure you want to leave the group \"%s\"?", "Leave_Livechat_Warning": "Are you sure you want to leave the livechat with \"%s\"?", @@ -1587,6 +1589,7 @@ "Send_invitation_email_info": "You can send multiple email invitations at once.", "Send_invitation_email_success": "You have successfully sent an invitation email to the following addresses:", "Send_request_on_chat_close": "Send Request on Chat Close", + "Send_request_on_lead_capture": "Send request on lead capture", "Send_request_on_offline_messages": "Send Request on Offline Messages", "Send_Test": "Send Test", "Send_welcome_email": "Send welcome email", @@ -2058,4 +2061,4 @@ "your_message_optional": "your message (optional)", "Your_password_is_wrong": "Your password is wrong!", "Your_push_was_sent_to_s_devices": "Your push was sent to %s devices" -} \ No newline at end of file +} diff --git a/packages/rocketchat-lib/package.js b/packages/rocketchat-lib/package.js index d7eedb158ad6..48c4c7217eae 100644 --- a/packages/rocketchat-lib/package.js +++ b/packages/rocketchat-lib/package.js @@ -97,6 +97,7 @@ Package.onUse(function(api) { api.addFiles('server/functions/deleteUser.js', 'server'); api.addFiles('server/functions/getFullUserData.js', 'server'); api.addFiles('server/functions/getRoomByNameOrIdWithOptionToJoin.js', 'server'); + api.addFiles('server/functions/loadMessageHistory.js', 'server'); api.addFiles('server/functions/removeUserFromRoom.js', 'server'); api.addFiles('server/functions/saveUser.js', 'server'); api.addFiles('server/functions/saveCustomFields.js', 'server'); diff --git a/packages/rocketchat-lib/server/functions/Notifications.js b/packages/rocketchat-lib/server/functions/Notifications.js index 1ebd77f6e8d9..808d0b8bb902 100644 --- a/packages/rocketchat-lib/server/functions/Notifications.js +++ b/packages/rocketchat-lib/server/functions/Notifications.js @@ -25,10 +25,7 @@ RocketChat.Notifications = new class { this.streamUser.allowWrite('logged'); this.streamAll.allowRead('all'); this.streamLogged.allowRead('logged'); - this.streamRoom.allowRead(function(eventName) { - if (this.userId == null) { - return false; - } + this.streamRoom.allowRead(function(eventName, extraData) { const [roomId] = eventName.split('/'); const user = Meteor.users.findOne(this.userId, { fields: { @@ -40,9 +37,12 @@ RocketChat.Notifications = new class { console.warn(`Invalid streamRoom eventName: "${ eventName }"`); return false; } - if (room.t === 'l' && room.v._id === user._id) { + if (room.t === 'l' && extraData && extraData.token && room.v.token === extraData.token) { return true; } + if (this.userId == null) { + return false; + } return room.usernames.indexOf(user.username) > -1; }); this.streamRoomUsers.allowRead('none'); @@ -117,12 +117,21 @@ RocketChat.Notifications = new class { } }; -RocketChat.Notifications.streamRoom.allowWrite(function(eventName, username) { - const [, e] = eventName.split('/'); +RocketChat.Notifications.streamRoom.allowWrite(function(eventName, username, typing, extraData) { + const [roomId, e] = eventName.split('/'); if (e === 'webrtc') { return true; } if (e === 'typing') { + + // typing from livechat widget + if (extraData && extraData.token) { + const room = RocketChat.models.Rooms.findOneById(roomId); + if (room && room.t === 'l' && room.v.token === extraData.token) { + return true; + } + } + const user = Meteor.users.findOne(this.userId, { fields: { username: 1 diff --git a/packages/rocketchat-lib/server/functions/loadMessageHistory.js b/packages/rocketchat-lib/server/functions/loadMessageHistory.js new file mode 100644 index 000000000000..f09d9030353f --- /dev/null +++ b/packages/rocketchat-lib/server/functions/loadMessageHistory.js @@ -0,0 +1,88 @@ +import _ from 'underscore'; + +const hideMessagesOfType = []; + +RocketChat.settings.get(/Message_HideType_.+/, function(key, value) { + const type = key.replace('Message_HideType_', ''); + const types = type === 'mute_unmute' ? ['user-muted', 'user-unmuted'] : [type]; + + return types.forEach((type) => { + const index = hideMessagesOfType.indexOf(type); + + if (value === true && index === -1) { + return hideMessagesOfType.push(type); + } + + if (index > -1) { + return hideMessagesOfType.splice(index, 1); + } + }); +}); + +RocketChat.loadMessageHistory = function loadMessageHistory({ userId, rid, end, limit = 20, ls }) { + const options = { + sort: { + ts: -1 + }, + limit + }; + + if (!RocketChat.settings.get('Message_ShowEditedStatus')) { + options.fields = { + editedAt: 0 + }; + } + + let records; + if (end != null) { + records = RocketChat.models.Messages.findVisibleByRoomIdBeforeTimestampNotContainingTypes(rid, end, hideMessagesOfType, options).fetch(); + } else { + records = RocketChat.models.Messages.findVisibleByRoomIdNotContainingTypes(rid, hideMessagesOfType, options).fetch(); + } + + const UI_Use_Real_Name = RocketChat.settings.get('UI_Use_Real_Name') === true; + + const messages = records.map((message) => { + message.starred = _.findWhere(message.starred, { + _id: userId + }); + if (message.u && message.u._id && UI_Use_Real_Name) { + const user = RocketChat.models.Users.findOneById(message.u._id); + message.u.name = user && user.name; + } + if (message.mentions && message.mentions.length && UI_Use_Real_Name) { + message.mentions.forEach((mention) => { + const user = RocketChat.models.Users.findOneById(mention._id); + mention.name = user && user.name; + }); + } + return message; + }); + + let unreadNotLoaded = 0; + let firstUnread; + + if (ls != null) { + const firstMessage = messages[messages.length - 1]; + + if ((firstMessage != null ? firstMessage.ts : undefined) > ls) { + delete options.limit; + + const unreadMessages = RocketChat.models.Messages.findVisibleByRoomIdBetweenTimestampsNotContainingTypes(rid, ls, firstMessage.ts, hideMessagesOfType, { + limit: 1, + sort: { + ts: 1 + } + }); + + firstUnread = unreadMessages.fetch()[0]; + unreadNotLoaded = unreadMessages.count(); + } + } + + return { + messages, + firstUnread, + unreadNotLoaded + }; +}; diff --git a/packages/rocketchat-lib/server/lib/sendNotificationsOnMessage.js b/packages/rocketchat-lib/server/lib/sendNotificationsOnMessage.js index 401491bbec6e..dab8e6de7fa0 100644 --- a/packages/rocketchat-lib/server/lib/sendNotificationsOnMessage.js +++ b/packages/rocketchat-lib/server/lib/sendNotificationsOnMessage.js @@ -166,6 +166,11 @@ RocketChat.callbacks.add('afterSaveMessage', function(message, room, userId) { const user = RocketChat.models.Users.findOneById(message.u._id); + // might be a livechat visitor + if (!user) { + return message; + } + /* Increment unread couter if direct messages */ diff --git a/packages/rocketchat-livechat/app/.meteor/.finished-upgraders b/packages/rocketchat-livechat/.app/.meteor/.finished-upgraders similarity index 100% rename from packages/rocketchat-livechat/app/.meteor/.finished-upgraders rename to packages/rocketchat-livechat/.app/.meteor/.finished-upgraders diff --git a/packages/rocketchat-livechat/app/.meteor/.gitignore b/packages/rocketchat-livechat/.app/.meteor/.gitignore similarity index 100% rename from packages/rocketchat-livechat/app/.meteor/.gitignore rename to packages/rocketchat-livechat/.app/.meteor/.gitignore diff --git a/packages/rocketchat-livechat/app/.meteor/.id b/packages/rocketchat-livechat/.app/.meteor/.id similarity index 100% rename from packages/rocketchat-livechat/app/.meteor/.id rename to packages/rocketchat-livechat/.app/.meteor/.id diff --git a/packages/rocketchat-livechat/app/.meteor/packages b/packages/rocketchat-livechat/.app/.meteor/packages similarity index 97% rename from packages/rocketchat-livechat/app/.meteor/packages rename to packages/rocketchat-livechat/.app/.meteor/packages index 3735ede0dccc..f688a528da4f 100644 --- a/packages/rocketchat-livechat/app/.meteor/packages +++ b/packages/rocketchat-livechat/.app/.meteor/packages @@ -38,3 +38,5 @@ standard-minifier-css@1.3.5 standard-minifier-js@2.2.0 shell-server@0.3.0 dynamic-import@0.2.0 + +konecty:user-presence diff --git a/packages/rocketchat-livechat/app/.meteor/platforms b/packages/rocketchat-livechat/.app/.meteor/platforms similarity index 100% rename from packages/rocketchat-livechat/app/.meteor/platforms rename to packages/rocketchat-livechat/.app/.meteor/platforms diff --git a/packages/rocketchat-livechat/app/.meteor/release b/packages/rocketchat-livechat/.app/.meteor/release similarity index 100% rename from packages/rocketchat-livechat/app/.meteor/release rename to packages/rocketchat-livechat/.app/.meteor/release diff --git a/packages/rocketchat-livechat/app/.meteor/versions b/packages/rocketchat-livechat/.app/.meteor/versions similarity index 97% rename from packages/rocketchat-livechat/app/.meteor/versions rename to packages/rocketchat-livechat/.app/.meteor/versions index b421e82bb665..9de32a733006 100644 --- a/packages/rocketchat-livechat/app/.meteor/versions +++ b/packages/rocketchat-livechat/.app/.meteor/versions @@ -39,6 +39,7 @@ jquery@1.11.10 kadira:blaze-layout@2.3.0 kadira:flow-router@2.12.1 konecty:nrr@2.0.2 +konecty:user-presence@2.0.0 less@2.7.11 livedata@1.0.18 localstorage@1.2.0 @@ -56,6 +57,7 @@ momentjs:moment@2.20.1 mongo@1.3.1 mongo-dev-server@1.1.0 mongo-id@1.0.6 +nooitaf:colors@1.1.2_1 npm-bcrypt@0.9.3 npm-mongo@2.2.33 observe-sequence@1.0.16 diff --git a/packages/rocketchat-livechat/app/client/components/modal.html b/packages/rocketchat-livechat/.app/client/components/modal.html similarity index 100% rename from packages/rocketchat-livechat/app/client/components/modal.html rename to packages/rocketchat-livechat/.app/client/components/modal.html diff --git a/packages/rocketchat-livechat/app/client/lib/CustomFields.js b/packages/rocketchat-livechat/.app/client/lib/CustomFields.js similarity index 89% rename from packages/rocketchat-livechat/app/client/lib/CustomFields.js rename to packages/rocketchat-livechat/.app/client/lib/CustomFields.js index 4f984139c7db..36132c9aa714 100644 --- a/packages/rocketchat-livechat/app/client/lib/CustomFields.js +++ b/packages/rocketchat-livechat/.app/client/lib/CustomFields.js @@ -1,3 +1,5 @@ +import visitor from '../../imports/client/visitor'; + this.CustomFields = (function() { let queue = {}; let initiated = false; @@ -13,7 +15,7 @@ this.CustomFields = (function() { const init = function() { Tracker.autorun(function() { - if (Meteor.userId()) { + if (visitor.getId()) { initiated = true; Object.keys(queue).forEach((key) => { setCustomField.call(this, queue[key].token, key, queue[key].value, queue[key].overwrite); diff --git a/packages/rocketchat-livechat/app/client/lib/LivechatVideoCall.js b/packages/rocketchat-livechat/.app/client/lib/LivechatVideoCall.js similarity index 97% rename from packages/rocketchat-livechat/app/client/lib/LivechatVideoCall.js rename to packages/rocketchat-livechat/.app/client/lib/LivechatVideoCall.js index 88bdd62e0f2c..e31490bb45f0 100644 --- a/packages/rocketchat-livechat/app/client/lib/LivechatVideoCall.js +++ b/packages/rocketchat-livechat/.app/client/lib/LivechatVideoCall.js @@ -1,4 +1,5 @@ /* globals LivechatVideoCall, cordova, JitsiMeetExternalAPI */ +import visitor from '../../imports/client/visitor'; LivechatVideoCall = new (class LivechatVideoCall { constructor() { diff --git a/packages/rocketchat-livechat/app/client/lib/_livechat.js b/packages/rocketchat-livechat/.app/client/lib/_livechat.js similarity index 93% rename from packages/rocketchat-livechat/app/client/lib/_livechat.js rename to packages/rocketchat-livechat/.app/client/lib/_livechat.js index eb94f7b84974..c0185a16b436 100644 --- a/packages/rocketchat-livechat/app/client/lib/_livechat.js +++ b/packages/rocketchat-livechat/.app/client/lib/_livechat.js @@ -1,3 +1,5 @@ +import visitor from '../../imports/client/visitor'; + this.Livechat = new (class Livechat { constructor() { this._online = new ReactiveVar(null); @@ -30,17 +32,17 @@ this.Livechat = new (class Livechat { this.stream = new Meteor.Streamer('livechat-room'); Tracker.autorun(() => { - if (this._room.get() && Meteor.userId()) { + if (this._room.get() && visitor.getId()) { RoomHistoryManager.getMoreIfIsEmpty(this._room.get()); visitor.subscribeToRoom(this._room.get()); visitor.setRoom(this._room.get()); - Meteor.call('livechat:getAgentData', this._room.get(), (error, result) => { + Meteor.call('livechat:getAgentData', { roomId: this._room.get(), token: visitor.getToken() }, (error, result) => { if (!error) { this._agent.set(result); } }); - this.stream.on(this._room.get(), (eventData) => { + this.stream.on(this._room.get(), { token: visitor.getToken() }, (eventData) => { if (!eventData || !eventData.type) { return; } diff --git a/packages/rocketchat-livechat/app/client/lib/autolinker.js b/packages/rocketchat-livechat/.app/client/lib/autolinker.js similarity index 100% rename from packages/rocketchat-livechat/app/client/lib/autolinker.js rename to packages/rocketchat-livechat/.app/client/lib/autolinker.js diff --git a/packages/rocketchat-livechat/app/client/lib/chatMessages.js b/packages/rocketchat-livechat/.app/client/lib/chatMessages.js similarity index 89% rename from packages/rocketchat-livechat/app/client/lib/chatMessages.js rename to packages/rocketchat-livechat/.app/client/lib/chatMessages.js index cb41d52ef9e9..664f7df344a8 100644 --- a/packages/rocketchat-livechat/app/client/lib/chatMessages.js +++ b/packages/rocketchat-livechat/.app/client/lib/chatMessages.js @@ -2,6 +2,7 @@ import _ from 'underscore'; import s from 'underscore.string'; import toastr from 'toastr'; +import visitor from '../../imports/client/visitor'; this.ChatMessages = class ChatMessages { init(node) { @@ -63,7 +64,7 @@ this.ChatMessages = class ChatMessages { } this.clearEditing(); const id = element.getAttribute('id'); - const message = ChatMessage.findOne({ _id: id, 'u._id': Meteor.userId() }); + const message = ChatMessage.findOne({ _id: id, 'u._id': visitor.getId() }); this.input.value = message.msg; this.editing.element = element; this.editing.index = index || this.getEditingIndex(element); @@ -122,12 +123,14 @@ this.ChatMessages = class ChatMessages { ChatMessage.update(result._id, _.omit(result, '_id')); Livechat.room = result.rid; + visitor.setConnected(); + parentCall('callback', 'chat-started'); } }); }; - if (!Meteor.userId()) { + if (!visitor.getId()) { const guest = { token: visitor.getToken() }; @@ -141,13 +144,8 @@ this.ChatMessages = class ChatMessages { return showError(error.reason); } - Meteor.loginWithToken(result.token, (error) => { - if (error) { - return showError(error.reason); - } - - sendMessage(); - }); + visitor.setId(result._id); + sendMessage(); }); } else { sendMessage(); @@ -189,20 +187,6 @@ this.ChatMessages = class ChatMessages { } } - tryCompletion(input) { - let value = input.value.match(/[^\s]+$/); - if (value && value.length > 0) { - value = value[0]; - - const re = new RegExp(value, 'i'); - - const user = Meteor.users.findOne({ username: re }, { fields: { username: 1 } }); - if (user) { - input.value = input.value.replace(value, `@${ user.username } `); - } - } - } - keyup(rid, event) { let i; const input = event.currentTarget; @@ -247,12 +231,6 @@ this.ChatMessages = class ChatMessages { return; } - if (k === 9) { - event.preventDefault(); - event.stopPropagation(); - this.tryCompletion(input); - } - if (k === 27) { if (this.editing.id) { event.preventDefault(); diff --git a/packages/rocketchat-livechat/app/client/lib/collections.js b/packages/rocketchat-livechat/.app/client/lib/collections.js similarity index 100% rename from packages/rocketchat-livechat/app/client/lib/collections.js rename to packages/rocketchat-livechat/.app/client/lib/collections.js diff --git a/packages/rocketchat-livechat/app/client/lib/commands.js b/packages/rocketchat-livechat/.app/client/lib/commands.js similarity index 79% rename from packages/rocketchat-livechat/app/client/lib/commands.js rename to packages/rocketchat-livechat/.app/client/lib/commands.js index 57c21c2951a7..e63bc3d10bf2 100644 --- a/packages/rocketchat-livechat/app/client/lib/commands.js +++ b/packages/rocketchat-livechat/.app/client/lib/commands.js @@ -1,4 +1,6 @@ /* globals LivechatVideoCall, Livechat, swal */ +import visitor from '../../imports/client/visitor'; + // Functions to call on messages of type 'command' this.Commands = { survey() { @@ -13,8 +15,8 @@ this.Commands = { promptTranscript() { if (Livechat.transcript) { - const user = Meteor.user(); - const email = user.visitorEmails && user.visitorEmails.length > 0 ? user.visitorEmails[0].address : ''; + const visitorData = visitor.getData(); + const email = visitorData && visitorData.visitorEmails && visitorData.visitorEmails.length > 0 ? visitorData.visitorEmails[0].address : ''; swal({ title: t('Chat_ended'), @@ -38,7 +40,7 @@ this.Commands = { swal.showInputError(t('please enter your email')); return false; } else { - Meteor.call('livechat:sendTranscript', visitor.getRoom(), response, (err) => { + Meteor.call('livechat:sendTranscript', visitor.getToken(), visitor.getRoom(), response, (err) => { if (err) { console.error(err); } diff --git a/packages/rocketchat-livechat/app/client/lib/error.js b/packages/rocketchat-livechat/.app/client/lib/error.js similarity index 100% rename from packages/rocketchat-livechat/app/client/lib/error.js rename to packages/rocketchat-livechat/.app/client/lib/error.js diff --git a/packages/rocketchat-livechat/app/client/lib/fromApp/Notifications.js b/packages/rocketchat-livechat/.app/client/lib/fromApp/Notifications.js similarity index 73% rename from packages/rocketchat-livechat/app/client/lib/fromApp/Notifications.js rename to packages/rocketchat-livechat/.app/client/lib/fromApp/Notifications.js index 4c919a20b0d6..8acbe3d5da12 100644 --- a/packages/rocketchat-livechat/app/client/lib/fromApp/Notifications.js +++ b/packages/rocketchat-livechat/.app/client/lib/fromApp/Notifications.js @@ -1,12 +1,14 @@ +import visitor from '../../../imports/client/visitor'; + this.Notifications = new class { constructor() { - this.logged = Meteor.userId() !== null; + this.logged = visitor.getId() !== null; this.loginCb = []; Tracker.autorun(() => { - if (Meteor.userId() !== null && this.logged === false) { + if (visitor.getId() !== null && this.logged === false) { this.loginCb.forEach(cb => cb()); } - return this.logged = Meteor.userId() !== null; + return this.logged = visitor.getId() !== null; }); this.debug = false; this.streamAll = new Meteor.Streamer('notify-all'); @@ -45,23 +47,23 @@ this.Notifications = new class { return this.streamUser.emit.apply(this.streamUser, args); } onAll(eventName, callback) { - return this.streamAll.on(eventName, callback); + return this.streamAll.on(eventName, { token: visitor.getToken() }, callback); } onLogged(eventName, callback) { return this.onLogin(() => { - return this.streamLogged.on(eventName, callback); + return this.streamLogged.on(eventName, { token: visitor.getToken() }, callback); }); } onRoom(room, eventName, callback) { if (this.debug === true) { - this.streamRoom.on(room, function() { + this.streamRoom.on(room, { token: visitor.getToken() }, function() { return console.log(`RocketChat.Notifications: onRoom ${ room }`, arguments); }); } - return this.streamRoom.on(`${ room }/${ eventName }`, callback); + return this.streamRoom.on(`${ room }/${ eventName }`, { token: visitor.getToken() }, callback); } onUser(eventName, callback) { - return this.streamUser.on(`${ Meteor.userId() }/${ eventName }`, callback); + return this.streamUser.on(`${ visitor.getId() }/${ eventName }`, { token: visitor.getToken() }, callback); } unAll(callback) { return this.streamAll.removeListener('notify', callback); @@ -73,7 +75,7 @@ this.Notifications = new class { return this.streamRoom.removeListener(`${ room }/${ eventName }`, callback); } unUser(eventName, callback) { - return this.streamUser.removeListener(`${ Meteor.userId() }/${ eventName }`, callback); + return this.streamUser.removeListener(`${ visitor.getId() }/${ eventName }`, callback); } }; diff --git a/packages/rocketchat-livechat/app/client/lib/fromApp/RoomHistoryManager.js b/packages/rocketchat-livechat/.app/client/lib/fromApp/RoomHistoryManager.js similarity index 97% rename from packages/rocketchat-livechat/app/client/lib/fromApp/RoomHistoryManager.js rename to packages/rocketchat-livechat/.app/client/lib/fromApp/RoomHistoryManager.js index c53a671576d0..2d6ab8f92441 100644 --- a/packages/rocketchat-livechat/app/client/lib/fromApp/RoomHistoryManager.js +++ b/packages/rocketchat-livechat/.app/client/lib/fromApp/RoomHistoryManager.js @@ -1,4 +1,6 @@ /* globals readMessage UserRoles RoomRoles*/ + +import visitor from '../../../imports/client/visitor'; import _ from 'underscore'; export const RoomHistoryManager = new class { @@ -42,7 +44,7 @@ export const RoomHistoryManager = new class { ts = new Date(); } - Meteor.call('loadHistory', rid, ts, limit, undefined, (err, result) => { + Meteor.call('livechat:loadHistory', { token: visitor.getToken(), rid, ts, limit }, (err, result) => { if (err) { return; } diff --git a/packages/rocketchat-livechat/app/client/lib/fromApp/avatar.js b/packages/rocketchat-livechat/.app/client/lib/fromApp/avatar.js similarity index 100% rename from packages/rocketchat-livechat/app/client/lib/fromApp/avatar.js rename to packages/rocketchat-livechat/.app/client/lib/fromApp/avatar.js diff --git a/packages/rocketchat-livechat/app/client/lib/hooks.js b/packages/rocketchat-livechat/.app/client/lib/hooks.js similarity index 95% rename from packages/rocketchat-livechat/app/client/lib/hooks.js rename to packages/rocketchat-livechat/.app/client/lib/hooks.js index eb7a2e34c56c..3a525c34cf5d 100644 --- a/packages/rocketchat-livechat/app/client/lib/hooks.js +++ b/packages/rocketchat-livechat/.app/client/lib/hooks.js @@ -1,4 +1,6 @@ /* globals CustomFields, Livechat */ +import visitor from '../../imports/client/visitor'; + const api = { pageVisited(info) { if (info.change === 'url') { diff --git a/packages/rocketchat-livechat/app/client/lib/msgTyping.js b/packages/rocketchat-livechat/.app/client/lib/msgTyping.js similarity index 79% rename from packages/rocketchat-livechat/app/client/lib/msgTyping.js rename to packages/rocketchat-livechat/.app/client/lib/msgTyping.js index aa00b0ad5fe9..378af82a34aa 100644 --- a/packages/rocketchat-livechat/app/client/lib/msgTyping.js +++ b/packages/rocketchat-livechat/.app/client/lib/msgTyping.js @@ -1,4 +1,5 @@ /* globals Notifications */ +import visitor from '../../imports/client/visitor'; import _ from 'underscore'; export const MsgTyping = (function() { @@ -15,7 +16,7 @@ export const MsgTyping = (function() { return; } usersTyping[room] = { users: {} }; - return Notifications.onRoom(room, 'typing', function(username, typing) { + return Notifications.onRoom(room, 'typing', function(username, typing, extraData) { const user = Meteor.user(); if (username === (user && user.username)) { return; @@ -36,7 +37,7 @@ export const MsgTyping = (function() { }; Tracker.autorun(() => { - if (visitor.getRoom() && Meteor.userId()) { + if (visitor.getRoom() && visitor.getId()) { addStream(visitor.getRoom()); } }); @@ -48,8 +49,8 @@ export const MsgTyping = (function() { clearTimeout(timeouts[room]); timeouts[room] = null; } - const user = Meteor.user(); - return Notifications.notifyRoom(room, 'typing', user && user.username, false); + const visitorData = visitor.getData(); + return Notifications.notifyRoom(room, 'typing', visitorData && visitorData.username, false, { token: visitor.getToken() }); }; const start = function(room) { if (!renew) { return; } @@ -58,8 +59,8 @@ export const MsgTyping = (function() { renew = false; selfTyping.set(true); - const user = Meteor.user(); - Notifications.notifyRoom(room, 'typing', user && user.username, true); + const visitorData = visitor.getData(); + Notifications.notifyRoom(room, 'typing', visitorData && visitorData.username, true, { token: visitor.getToken() }); clearTimeout(timeouts[room]); return timeouts[room] = Meteor.setTimeout(() => stop(room), timeout); }; diff --git a/packages/rocketchat-livechat/app/client/lib/parentCall.js b/packages/rocketchat-livechat/.app/client/lib/parentCall.js similarity index 100% rename from packages/rocketchat-livechat/app/client/lib/parentCall.js rename to packages/rocketchat-livechat/.app/client/lib/parentCall.js diff --git a/packages/rocketchat-livechat/app/client/lib/tapi18n.js b/packages/rocketchat-livechat/.app/client/lib/tapi18n.js similarity index 100% rename from packages/rocketchat-livechat/app/client/lib/tapi18n.js rename to packages/rocketchat-livechat/.app/client/lib/tapi18n.js diff --git a/packages/rocketchat-livechat/app/client/lib/triggers.js b/packages/rocketchat-livechat/.app/client/lib/triggers.js similarity index 95% rename from packages/rocketchat-livechat/app/client/lib/triggers.js rename to packages/rocketchat-livechat/.app/client/lib/triggers.js index e303ef110937..72f68df99294 100644 --- a/packages/rocketchat-livechat/app/client/lib/triggers.js +++ b/packages/rocketchat-livechat/.app/client/lib/triggers.js @@ -1,3 +1,5 @@ +import visitor from '../../imports/client/visitor'; + this.Triggers = (function() { let triggers = []; let initiated = false; @@ -5,7 +7,7 @@ this.Triggers = (function() { let enabled = true; const fire = function(trigger) { - if (!enabled || Meteor.userId()) { + if (!enabled || visitor.getId()) { return; } trigger.actions.forEach(function(action) { diff --git a/packages/rocketchat-livechat/app/client/methods/sendMessageExternal.js b/packages/rocketchat-livechat/.app/client/methods/sendMessageExternal.js similarity index 86% rename from packages/rocketchat-livechat/app/client/methods/sendMessageExternal.js rename to packages/rocketchat-livechat/.app/client/methods/sendMessageExternal.js index 0169a7e7bb09..8a2bdaebf2a7 100644 --- a/packages/rocketchat-livechat/app/client/methods/sendMessageExternal.js +++ b/packages/rocketchat-livechat/.app/client/methods/sendMessageExternal.js @@ -1,3 +1,4 @@ +import visitor from '../../imports/client/visitor'; import s from 'underscore.string'; Meteor.methods({ @@ -12,7 +13,7 @@ Meteor.methods({ const user = Meteor.user(); message.u = { - _id: Meteor.userId(), + _id: visitor.getId(), username: user && user.username || 'visitor' }; diff --git a/packages/rocketchat-livechat/app/client/routes/router.js b/packages/rocketchat-livechat/.app/client/routes/router.js similarity index 79% rename from packages/rocketchat-livechat/app/client/routes/router.js rename to packages/rocketchat-livechat/.app/client/routes/router.js index 6762a019a5e2..9b6e69af2952 100644 --- a/packages/rocketchat-livechat/app/client/routes/router.js +++ b/packages/rocketchat-livechat/.app/client/routes/router.js @@ -1,3 +1,5 @@ +import visitor from '../../imports/client/visitor'; + BlazeLayout.setRoot('body'); FlowRouter.route('/livechat', { diff --git a/packages/rocketchat-livechat/app/client/startup/customFields.js b/packages/rocketchat-livechat/.app/client/startup/customFields.js similarity index 100% rename from packages/rocketchat-livechat/app/client/startup/customFields.js rename to packages/rocketchat-livechat/.app/client/startup/customFields.js diff --git a/packages/rocketchat-livechat/.app/client/startup/visitor.js b/packages/rocketchat-livechat/.app/client/startup/visitor.js new file mode 100644 index 000000000000..92b723bc05fa --- /dev/null +++ b/packages/rocketchat-livechat/.app/client/startup/visitor.js @@ -0,0 +1,29 @@ +import visitor from '../../imports/client/visitor'; + +Meteor.startup(() => { + if (!localStorage.getItem('rocketChatLivechat')) { + localStorage.setItem('rocketChatLivechat', Random.id()); + } else { + Tracker.autorun(c => { + if (!visitor.getId() && visitor.getToken()) { + Meteor.call('livechat:loginByToken', visitor.getToken(), (err, result) => { + if (result && result._id) { + visitor.setId(result._id); + c.stop(); + } + }); + } + }); + } +}); + +Meteor.startup(() => { + let connected = false; + Tracker.autorun(function() { + var connectionStatus = Meteor.status(); + if (visitor.getRoom() && visitor.getToken() && connectionStatus.connected && !connected) { + connected = connectionStatus.connected; + visitor.setConnected(); + } + }); +}); diff --git a/packages/rocketchat-livechat/app/client/stylesheets/main.less b/packages/rocketchat-livechat/.app/client/stylesheets/main.less similarity index 97% rename from packages/rocketchat-livechat/app/client/stylesheets/main.less rename to packages/rocketchat-livechat/.app/client/stylesheets/main.less index afad4f334141..695ebb332081 100644 --- a/packages/rocketchat-livechat/app/client/stylesheets/main.less +++ b/packages/rocketchat-livechat/.app/client/stylesheets/main.less @@ -340,6 +340,10 @@ input:focus { left: inherit; } } + + .thumb { + display: none; + } } .delete-message { @@ -443,18 +447,23 @@ input:focus { text-decoration: line-through; } - .avatar .avatar-image { + .avatar { height: 100%; width: 100%; - min-height: 20px; - min-width: 20px; - display: block; - position: relative; - background-color: transparent; - background-size: cover; - background-repeat: no-repeat; - background-position: center; - border-radius: 4px; + + .avatar-image { + height: 100%; + width: 100%; + min-height: 20px; + min-width: 20px; + display: block; + position: relative; + background-color: transparent; + background-size: cover; + background-repeat: no-repeat; + background-position: center; + border-radius: 4px; + } } } } diff --git a/packages/rocketchat-livechat/app/client/stylesheets/utils/_keyframes.import.less b/packages/rocketchat-livechat/.app/client/stylesheets/utils/_keyframes.import.less similarity index 100% rename from packages/rocketchat-livechat/app/client/stylesheets/utils/_keyframes.import.less rename to packages/rocketchat-livechat/.app/client/stylesheets/utils/_keyframes.import.less diff --git a/packages/rocketchat-livechat/app/client/stylesheets/utils/_loading.import.less b/packages/rocketchat-livechat/.app/client/stylesheets/utils/_loading.import.less similarity index 100% rename from packages/rocketchat-livechat/app/client/stylesheets/utils/_loading.import.less rename to packages/rocketchat-livechat/.app/client/stylesheets/utils/_loading.import.less diff --git a/packages/rocketchat-livechat/app/client/stylesheets/utils/_reset.import.less b/packages/rocketchat-livechat/.app/client/stylesheets/utils/_reset.import.less similarity index 100% rename from packages/rocketchat-livechat/app/client/stylesheets/utils/_reset.import.less rename to packages/rocketchat-livechat/.app/client/stylesheets/utils/_reset.import.less diff --git a/packages/rocketchat-livechat/app/client/stylesheets/utils/_variables.import.less b/packages/rocketchat-livechat/.app/client/stylesheets/utils/_variables.import.less similarity index 100% rename from packages/rocketchat-livechat/app/client/stylesheets/utils/_variables.import.less rename to packages/rocketchat-livechat/.app/client/stylesheets/utils/_variables.import.less diff --git a/packages/rocketchat-livechat/app/client/views/avatar.html b/packages/rocketchat-livechat/.app/client/views/avatar.html similarity index 100% rename from packages/rocketchat-livechat/app/client/views/avatar.html rename to packages/rocketchat-livechat/.app/client/views/avatar.html diff --git a/packages/rocketchat-livechat/app/client/views/avatar.js b/packages/rocketchat-livechat/.app/client/views/avatar.js similarity index 82% rename from packages/rocketchat-livechat/app/client/views/avatar.js rename to packages/rocketchat-livechat/.app/client/views/avatar.js index 5ab46c366a45..62443cf49912 100644 --- a/packages/rocketchat-livechat/app/client/views/avatar.js +++ b/packages/rocketchat-livechat/.app/client/views/avatar.js @@ -1,3 +1,5 @@ +import visitor from '../../imports/client/visitor'; + Template.avatar.helpers({ imageUrl() { let username = this.username; @@ -6,7 +8,7 @@ Template.avatar.helpers({ username = user && user.username; } - const currentUser = Meteor.users.findOne(Meteor.userId(), { fields: { username: 1 }}); + const currentUser = visitor.getData(); if (!username || (currentUser && currentUser.username === username)) { return; } diff --git a/packages/rocketchat-livechat/app/client/views/livechatWindow.html b/packages/rocketchat-livechat/.app/client/views/livechatWindow.html similarity index 100% rename from packages/rocketchat-livechat/app/client/views/livechatWindow.html rename to packages/rocketchat-livechat/.app/client/views/livechatWindow.html diff --git a/packages/rocketchat-livechat/app/client/views/livechatWindow.js b/packages/rocketchat-livechat/.app/client/views/livechatWindow.js similarity index 94% rename from packages/rocketchat-livechat/app/client/views/livechatWindow.js rename to packages/rocketchat-livechat/.app/client/views/livechatWindow.js index fcbe88f9587d..7651415e6b48 100644 --- a/packages/rocketchat-livechat/app/client/views/livechatWindow.js +++ b/packages/rocketchat-livechat/.app/client/views/livechatWindow.js @@ -1,4 +1,5 @@ /* globals Department, Livechat, LivechatVideoCall */ +import visitor from '../../imports/client/visitor'; Template.livechatWindow.helpers({ title() { @@ -17,7 +18,7 @@ Template.livechatWindow.helpers({ return Session.get('sound'); }, showRegisterForm() { - if (Session.get('triggered') || Meteor.userId()) { + if (Session.get('triggered') || visitor.getId()) { return false; } return Livechat.registrationForm; @@ -114,6 +115,12 @@ Template.livechatWindow.onCreated(function() { if (result.room) { Livechat.room = result.room._id; + + visitor.setConnected(); + } + + if (result.visitor) { + visitor.setData(result.visitor); } if (result.agentData) { diff --git a/packages/rocketchat-livechat/app/client/views/loading.html b/packages/rocketchat-livechat/.app/client/views/loading.html similarity index 100% rename from packages/rocketchat-livechat/app/client/views/loading.html rename to packages/rocketchat-livechat/.app/client/views/loading.html diff --git a/packages/rocketchat-livechat/app/client/views/main.html b/packages/rocketchat-livechat/.app/client/views/main.html similarity index 100% rename from packages/rocketchat-livechat/app/client/views/main.html rename to packages/rocketchat-livechat/.app/client/views/main.html diff --git a/packages/rocketchat-livechat/app/client/views/message.html b/packages/rocketchat-livechat/.app/client/views/message.html similarity index 100% rename from packages/rocketchat-livechat/app/client/views/message.html rename to packages/rocketchat-livechat/.app/client/views/message.html diff --git a/packages/rocketchat-livechat/app/client/views/message.js b/packages/rocketchat-livechat/.app/client/views/message.js similarity index 97% rename from packages/rocketchat-livechat/app/client/views/message.js rename to packages/rocketchat-livechat/.app/client/views/message.js index edf40fee5cd2..9dc31af875b1 100644 --- a/packages/rocketchat-livechat/app/client/views/message.js +++ b/packages/rocketchat-livechat/.app/client/views/message.js @@ -1,10 +1,11 @@ /* globals Livechat, t, tr, livechatAutolinker */ import moment from 'moment'; +import visitor from '../../imports/client/visitor'; import s from 'underscore.string'; Template.message.helpers({ own() { - if (this.u && this.u._id === Meteor.userId()) { + if (this.u && this.u._id === visitor.getId()) { return 'own'; } }, diff --git a/packages/rocketchat-livechat/app/client/views/messages.html b/packages/rocketchat-livechat/.app/client/views/messages.html similarity index 100% rename from packages/rocketchat-livechat/app/client/views/messages.html rename to packages/rocketchat-livechat/.app/client/views/messages.html diff --git a/packages/rocketchat-livechat/app/client/views/messages.js b/packages/rocketchat-livechat/.app/client/views/messages.js similarity index 96% rename from packages/rocketchat-livechat/app/client/views/messages.js rename to packages/rocketchat-livechat/.app/client/views/messages.js index 4b7820e35cec..fb457c0fd26a 100644 --- a/packages/rocketchat-livechat/app/client/views/messages.js +++ b/packages/rocketchat-livechat/.app/client/views/messages.js @@ -1,4 +1,5 @@ /* globals Livechat, LivechatVideoCall, MsgTyping */ +import visitor from '../../imports/client/visitor'; import _ from 'underscore'; Template.messages.helpers({ @@ -115,19 +116,14 @@ Template.messages.events({ 'click .video-button'(event) { event.preventDefault(); - if (!Meteor.userId()) { + if (!visitor.getId()) { Meteor.call('livechat:registerGuest', { token: visitor.getToken() }, (error, result) => { if (error) { return console.log(error.reason); } - Meteor.loginWithToken(result.token, (error) => { - if (error) { - return console.log(error.reason); - } - - LivechatVideoCall.request(); - }); + visitor.setId(result._id); + LivechatVideoCall.request(); }); } else { LivechatVideoCall.request(); diff --git a/packages/rocketchat-livechat/app/client/views/offlineForm.html b/packages/rocketchat-livechat/.app/client/views/offlineForm.html similarity index 100% rename from packages/rocketchat-livechat/app/client/views/offlineForm.html rename to packages/rocketchat-livechat/.app/client/views/offlineForm.html diff --git a/packages/rocketchat-livechat/app/client/views/offlineForm.js b/packages/rocketchat-livechat/.app/client/views/offlineForm.js similarity index 100% rename from packages/rocketchat-livechat/app/client/views/offlineForm.js rename to packages/rocketchat-livechat/.app/client/views/offlineForm.js diff --git a/packages/rocketchat-livechat/app/client/views/options.html b/packages/rocketchat-livechat/.app/client/views/options.html similarity index 100% rename from packages/rocketchat-livechat/app/client/views/options.html rename to packages/rocketchat-livechat/.app/client/views/options.html diff --git a/packages/rocketchat-livechat/app/client/views/options.js b/packages/rocketchat-livechat/.app/client/views/options.js similarity index 84% rename from packages/rocketchat-livechat/app/client/views/options.js rename to packages/rocketchat-livechat/.app/client/views/options.js index 653c916a68b7..c6c38c864c77 100644 --- a/packages/rocketchat-livechat/app/client/views/options.js +++ b/packages/rocketchat-livechat/.app/client/views/options.js @@ -1,4 +1,5 @@ /* globals Department, Livechat, swal */ +import visitor from '../../imports/client/visitor'; Template.options.helpers({ showDepartments() { @@ -25,7 +26,7 @@ Template.options.events({ closeOnConfirm: true, html: false }, () => { - Meteor.call('livechat:closeByVisitor', visitor.getRoom(), (error) => { + Meteor.call('livechat:closeByVisitor', { roomId: visitor.getRoom(), token: visitor.getToken() }, (error) => { if (error) { return console.log('Error ->', error); } diff --git a/packages/rocketchat-livechat/app/client/views/poweredBy.html b/packages/rocketchat-livechat/.app/client/views/poweredBy.html similarity index 100% rename from packages/rocketchat-livechat/app/client/views/poweredBy.html rename to packages/rocketchat-livechat/.app/client/views/poweredBy.html diff --git a/packages/rocketchat-livechat/app/client/views/register.html b/packages/rocketchat-livechat/.app/client/views/register.html similarity index 100% rename from packages/rocketchat-livechat/app/client/views/register.html rename to packages/rocketchat-livechat/.app/client/views/register.html diff --git a/packages/rocketchat-livechat/app/client/views/register.js b/packages/rocketchat-livechat/.app/client/views/register.js similarity index 93% rename from packages/rocketchat-livechat/app/client/views/register.js rename to packages/rocketchat-livechat/.app/client/views/register.js index f62c8341311e..f42fe1aa5b9f 100644 --- a/packages/rocketchat-livechat/app/client/views/register.js +++ b/packages/rocketchat-livechat/.app/client/views/register.js @@ -1,4 +1,5 @@ /* globals Department, Livechat, LivechatVideoCall */ +import visitor from '../../imports/client/visitor'; import _ from 'underscore'; Template.register.helpers({ @@ -57,12 +58,8 @@ Template.register.events({ return instance.showError(error.reason); } parentCall('callback', ['pre-chat-form-submit', _.omit(guest, 'token')]); - Meteor.loginWithToken(result.token, function(error) { - if (error) { - return instance.showError(error.reason); - } - start(); - }); + visitor.setId(result._id); + start(); }); } }, diff --git a/packages/rocketchat-livechat/app/client/views/survey.html b/packages/rocketchat-livechat/.app/client/views/survey.html similarity index 100% rename from packages/rocketchat-livechat/app/client/views/survey.html rename to packages/rocketchat-livechat/.app/client/views/survey.html diff --git a/packages/rocketchat-livechat/app/client/views/survey.js b/packages/rocketchat-livechat/.app/client/views/survey.js similarity index 90% rename from packages/rocketchat-livechat/app/client/views/survey.js rename to packages/rocketchat-livechat/.app/client/views/survey.js index 0bde47e041b8..c9c66add13a7 100644 --- a/packages/rocketchat-livechat/app/client/views/survey.js +++ b/packages/rocketchat-livechat/.app/client/views/survey.js @@ -1,4 +1,5 @@ /* globals swal */ +import visitor from '../../imports/client/visitor'; Template.survey.events({ 'click button.skip'(e, instance) { diff --git a/packages/rocketchat-livechat/app/client/views/switchDepartment.html b/packages/rocketchat-livechat/.app/client/views/switchDepartment.html similarity index 100% rename from packages/rocketchat-livechat/app/client/views/switchDepartment.html rename to packages/rocketchat-livechat/.app/client/views/switchDepartment.html diff --git a/packages/rocketchat-livechat/app/client/views/switchDepartment.js b/packages/rocketchat-livechat/.app/client/views/switchDepartment.js similarity index 90% rename from packages/rocketchat-livechat/app/client/views/switchDepartment.js rename to packages/rocketchat-livechat/.app/client/views/switchDepartment.js index a343e8d3334e..e6288ed920dd 100644 --- a/packages/rocketchat-livechat/app/client/views/switchDepartment.js +++ b/packages/rocketchat-livechat/.app/client/views/switchDepartment.js @@ -1,4 +1,5 @@ /* globals Department, Livechat, swal */ +import visitor from '../../imports/client/visitor'; Template.switchDepartment.helpers({ departments() { @@ -44,7 +45,7 @@ Template.switchDepartment.events({ closeOnConfirm: true, html: false }, () => { - Meteor.call('livechat:closeByVisitor', visitor.getRoom(), (error) => { + Meteor.call('livechat:closeByVisitor', { roomId: visitor.getRoom(), token: visitor.getToken() }, (error) => { if (error) { return console.log('Error ->', error); } diff --git a/packages/rocketchat-livechat/app/client/views/videoCall.html b/packages/rocketchat-livechat/.app/client/views/videoCall.html similarity index 100% rename from packages/rocketchat-livechat/app/client/views/videoCall.html rename to packages/rocketchat-livechat/.app/client/views/videoCall.html diff --git a/packages/rocketchat-livechat/app/client/views/videoCall.js b/packages/rocketchat-livechat/.app/client/views/videoCall.js similarity index 100% rename from packages/rocketchat-livechat/app/client/views/videoCall.js rename to packages/rocketchat-livechat/.app/client/views/videoCall.js diff --git a/packages/rocketchat-livechat/app/i18n/ar.i18n.json b/packages/rocketchat-livechat/.app/i18n/ar.i18n.json similarity index 100% rename from packages/rocketchat-livechat/app/i18n/ar.i18n.json rename to packages/rocketchat-livechat/.app/i18n/ar.i18n.json diff --git a/packages/rocketchat-livechat/app/i18n/az.i18n.json b/packages/rocketchat-livechat/.app/i18n/az.i18n.json similarity index 100% rename from packages/rocketchat-livechat/app/i18n/az.i18n.json rename to packages/rocketchat-livechat/.app/i18n/az.i18n.json diff --git a/packages/rocketchat-livechat/app/i18n/bg.i18n.json b/packages/rocketchat-livechat/.app/i18n/bg.i18n.json similarity index 100% rename from packages/rocketchat-livechat/app/i18n/bg.i18n.json rename to packages/rocketchat-livechat/.app/i18n/bg.i18n.json diff --git a/packages/rocketchat-livechat/app/i18n/ca.i18n.json b/packages/rocketchat-livechat/.app/i18n/ca.i18n.json similarity index 100% rename from packages/rocketchat-livechat/app/i18n/ca.i18n.json rename to packages/rocketchat-livechat/.app/i18n/ca.i18n.json diff --git a/packages/rocketchat-livechat/app/i18n/cs.i18n.json b/packages/rocketchat-livechat/.app/i18n/cs.i18n.json similarity index 100% rename from packages/rocketchat-livechat/app/i18n/cs.i18n.json rename to packages/rocketchat-livechat/.app/i18n/cs.i18n.json diff --git a/packages/rocketchat-livechat/app/i18n/da.i18n.json b/packages/rocketchat-livechat/.app/i18n/da.i18n.json similarity index 100% rename from packages/rocketchat-livechat/app/i18n/da.i18n.json rename to packages/rocketchat-livechat/.app/i18n/da.i18n.json diff --git a/packages/rocketchat-livechat/app/i18n/de-AT.i18n.json b/packages/rocketchat-livechat/.app/i18n/de-AT.i18n.json similarity index 100% rename from packages/rocketchat-livechat/app/i18n/de-AT.i18n.json rename to packages/rocketchat-livechat/.app/i18n/de-AT.i18n.json diff --git a/packages/rocketchat-livechat/app/i18n/de.i18n.json b/packages/rocketchat-livechat/.app/i18n/de.i18n.json similarity index 100% rename from packages/rocketchat-livechat/app/i18n/de.i18n.json rename to packages/rocketchat-livechat/.app/i18n/de.i18n.json diff --git a/packages/rocketchat-livechat/app/i18n/el.i18n.json b/packages/rocketchat-livechat/.app/i18n/el.i18n.json similarity index 100% rename from packages/rocketchat-livechat/app/i18n/el.i18n.json rename to packages/rocketchat-livechat/.app/i18n/el.i18n.json diff --git a/packages/rocketchat-livechat/app/i18n/en.i18n.json b/packages/rocketchat-livechat/.app/i18n/en.i18n.json similarity index 100% rename from packages/rocketchat-livechat/app/i18n/en.i18n.json rename to packages/rocketchat-livechat/.app/i18n/en.i18n.json diff --git a/packages/rocketchat-livechat/app/i18n/eo.i18n.json b/packages/rocketchat-livechat/.app/i18n/eo.i18n.json similarity index 100% rename from packages/rocketchat-livechat/app/i18n/eo.i18n.json rename to packages/rocketchat-livechat/.app/i18n/eo.i18n.json diff --git a/packages/rocketchat-livechat/app/i18n/es.i18n.json b/packages/rocketchat-livechat/.app/i18n/es.i18n.json similarity index 100% rename from packages/rocketchat-livechat/app/i18n/es.i18n.json rename to packages/rocketchat-livechat/.app/i18n/es.i18n.json diff --git a/packages/rocketchat-livechat/app/i18n/fa.i18n.json b/packages/rocketchat-livechat/.app/i18n/fa.i18n.json similarity index 100% rename from packages/rocketchat-livechat/app/i18n/fa.i18n.json rename to packages/rocketchat-livechat/.app/i18n/fa.i18n.json diff --git a/packages/rocketchat-livechat/app/i18n/fi.i18n.json b/packages/rocketchat-livechat/.app/i18n/fi.i18n.json similarity index 100% rename from packages/rocketchat-livechat/app/i18n/fi.i18n.json rename to packages/rocketchat-livechat/.app/i18n/fi.i18n.json diff --git a/packages/rocketchat-livechat/app/i18n/fr.i18n.json b/packages/rocketchat-livechat/.app/i18n/fr.i18n.json similarity index 100% rename from packages/rocketchat-livechat/app/i18n/fr.i18n.json rename to packages/rocketchat-livechat/.app/i18n/fr.i18n.json diff --git a/packages/rocketchat-livechat/app/i18n/he.i18n.json b/packages/rocketchat-livechat/.app/i18n/he.i18n.json similarity index 100% rename from packages/rocketchat-livechat/app/i18n/he.i18n.json rename to packages/rocketchat-livechat/.app/i18n/he.i18n.json diff --git a/packages/rocketchat-livechat/app/i18n/hr.i18n.json b/packages/rocketchat-livechat/.app/i18n/hr.i18n.json similarity index 100% rename from packages/rocketchat-livechat/app/i18n/hr.i18n.json rename to packages/rocketchat-livechat/.app/i18n/hr.i18n.json diff --git a/packages/rocketchat-livechat/app/i18n/hu.i18n.json b/packages/rocketchat-livechat/.app/i18n/hu.i18n.json similarity index 100% rename from packages/rocketchat-livechat/app/i18n/hu.i18n.json rename to packages/rocketchat-livechat/.app/i18n/hu.i18n.json diff --git a/packages/rocketchat-livechat/app/i18n/id.i18n.json b/packages/rocketchat-livechat/.app/i18n/id.i18n.json similarity index 100% rename from packages/rocketchat-livechat/app/i18n/id.i18n.json rename to packages/rocketchat-livechat/.app/i18n/id.i18n.json diff --git a/packages/rocketchat-livechat/app/i18n/it.i18n.json b/packages/rocketchat-livechat/.app/i18n/it.i18n.json similarity index 100% rename from packages/rocketchat-livechat/app/i18n/it.i18n.json rename to packages/rocketchat-livechat/.app/i18n/it.i18n.json diff --git a/packages/rocketchat-livechat/app/i18n/ja.i18n.json b/packages/rocketchat-livechat/.app/i18n/ja.i18n.json similarity index 100% rename from packages/rocketchat-livechat/app/i18n/ja.i18n.json rename to packages/rocketchat-livechat/.app/i18n/ja.i18n.json diff --git a/packages/rocketchat-livechat/app/i18n/km.i18n.json b/packages/rocketchat-livechat/.app/i18n/km.i18n.json similarity index 100% rename from packages/rocketchat-livechat/app/i18n/km.i18n.json rename to packages/rocketchat-livechat/.app/i18n/km.i18n.json diff --git a/packages/rocketchat-livechat/app/i18n/ko.i18n.json b/packages/rocketchat-livechat/.app/i18n/ko.i18n.json similarity index 100% rename from packages/rocketchat-livechat/app/i18n/ko.i18n.json rename to packages/rocketchat-livechat/.app/i18n/ko.i18n.json diff --git a/packages/rocketchat-livechat/app/i18n/ku.i18n.json b/packages/rocketchat-livechat/.app/i18n/ku.i18n.json similarity index 100% rename from packages/rocketchat-livechat/app/i18n/ku.i18n.json rename to packages/rocketchat-livechat/.app/i18n/ku.i18n.json diff --git a/packages/rocketchat-livechat/app/i18n/lo.i18n.json b/packages/rocketchat-livechat/.app/i18n/lo.i18n.json similarity index 100% rename from packages/rocketchat-livechat/app/i18n/lo.i18n.json rename to packages/rocketchat-livechat/.app/i18n/lo.i18n.json diff --git a/packages/rocketchat-livechat/app/i18n/lt.i18n.json b/packages/rocketchat-livechat/.app/i18n/lt.i18n.json similarity index 100% rename from packages/rocketchat-livechat/app/i18n/lt.i18n.json rename to packages/rocketchat-livechat/.app/i18n/lt.i18n.json diff --git a/packages/rocketchat-livechat/app/i18n/ms-MY.i18n.json b/packages/rocketchat-livechat/.app/i18n/ms-MY.i18n.json similarity index 100% rename from packages/rocketchat-livechat/app/i18n/ms-MY.i18n.json rename to packages/rocketchat-livechat/.app/i18n/ms-MY.i18n.json diff --git a/packages/rocketchat-livechat/app/i18n/nl.i18n.json b/packages/rocketchat-livechat/.app/i18n/nl.i18n.json similarity index 100% rename from packages/rocketchat-livechat/app/i18n/nl.i18n.json rename to packages/rocketchat-livechat/.app/i18n/nl.i18n.json diff --git a/packages/rocketchat-livechat/app/i18n/no.i18n.json b/packages/rocketchat-livechat/.app/i18n/no.i18n.json similarity index 100% rename from packages/rocketchat-livechat/app/i18n/no.i18n.json rename to packages/rocketchat-livechat/.app/i18n/no.i18n.json diff --git a/packages/rocketchat-livechat/app/i18n/pl.i18n.json b/packages/rocketchat-livechat/.app/i18n/pl.i18n.json similarity index 100% rename from packages/rocketchat-livechat/app/i18n/pl.i18n.json rename to packages/rocketchat-livechat/.app/i18n/pl.i18n.json diff --git a/packages/rocketchat-livechat/app/i18n/pt-BR.i18n.json b/packages/rocketchat-livechat/.app/i18n/pt-BR.i18n.json similarity index 100% rename from packages/rocketchat-livechat/app/i18n/pt-BR.i18n.json rename to packages/rocketchat-livechat/.app/i18n/pt-BR.i18n.json diff --git a/packages/rocketchat-livechat/app/i18n/pt.i18n.json b/packages/rocketchat-livechat/.app/i18n/pt.i18n.json similarity index 100% rename from packages/rocketchat-livechat/app/i18n/pt.i18n.json rename to packages/rocketchat-livechat/.app/i18n/pt.i18n.json diff --git a/packages/rocketchat-livechat/app/i18n/ro.i18n.json b/packages/rocketchat-livechat/.app/i18n/ro.i18n.json similarity index 100% rename from packages/rocketchat-livechat/app/i18n/ro.i18n.json rename to packages/rocketchat-livechat/.app/i18n/ro.i18n.json diff --git a/packages/rocketchat-livechat/app/i18n/ru.i18n.json b/packages/rocketchat-livechat/.app/i18n/ru.i18n.json similarity index 100% rename from packages/rocketchat-livechat/app/i18n/ru.i18n.json rename to packages/rocketchat-livechat/.app/i18n/ru.i18n.json diff --git a/packages/rocketchat-livechat/app/i18n/sq.i18n.json b/packages/rocketchat-livechat/.app/i18n/sq.i18n.json similarity index 100% rename from packages/rocketchat-livechat/app/i18n/sq.i18n.json rename to packages/rocketchat-livechat/.app/i18n/sq.i18n.json diff --git a/packages/rocketchat-livechat/app/i18n/sr.i18n.json b/packages/rocketchat-livechat/.app/i18n/sr.i18n.json similarity index 100% rename from packages/rocketchat-livechat/app/i18n/sr.i18n.json rename to packages/rocketchat-livechat/.app/i18n/sr.i18n.json diff --git a/packages/rocketchat-livechat/app/i18n/sv.i18n.json b/packages/rocketchat-livechat/.app/i18n/sv.i18n.json similarity index 100% rename from packages/rocketchat-livechat/app/i18n/sv.i18n.json rename to packages/rocketchat-livechat/.app/i18n/sv.i18n.json diff --git a/packages/rocketchat-livechat/app/i18n/ta-IN.i18n.json b/packages/rocketchat-livechat/.app/i18n/ta-IN.i18n.json similarity index 100% rename from packages/rocketchat-livechat/app/i18n/ta-IN.i18n.json rename to packages/rocketchat-livechat/.app/i18n/ta-IN.i18n.json diff --git a/packages/rocketchat-livechat/app/i18n/tr.i18n.json b/packages/rocketchat-livechat/.app/i18n/tr.i18n.json similarity index 100% rename from packages/rocketchat-livechat/app/i18n/tr.i18n.json rename to packages/rocketchat-livechat/.app/i18n/tr.i18n.json diff --git a/packages/rocketchat-livechat/app/i18n/ug.i18n.json b/packages/rocketchat-livechat/.app/i18n/ug.i18n.json similarity index 100% rename from packages/rocketchat-livechat/app/i18n/ug.i18n.json rename to packages/rocketchat-livechat/.app/i18n/ug.i18n.json diff --git a/packages/rocketchat-livechat/app/i18n/uk.i18n.json b/packages/rocketchat-livechat/.app/i18n/uk.i18n.json similarity index 100% rename from packages/rocketchat-livechat/app/i18n/uk.i18n.json rename to packages/rocketchat-livechat/.app/i18n/uk.i18n.json diff --git a/packages/rocketchat-livechat/app/i18n/zh-HK.i18n.json b/packages/rocketchat-livechat/.app/i18n/zh-HK.i18n.json similarity index 100% rename from packages/rocketchat-livechat/app/i18n/zh-HK.i18n.json rename to packages/rocketchat-livechat/.app/i18n/zh-HK.i18n.json diff --git a/packages/rocketchat-livechat/app/i18n/zh-TW.i18n.json b/packages/rocketchat-livechat/.app/i18n/zh-TW.i18n.json similarity index 100% rename from packages/rocketchat-livechat/app/i18n/zh-TW.i18n.json rename to packages/rocketchat-livechat/.app/i18n/zh-TW.i18n.json diff --git a/packages/rocketchat-livechat/app/i18n/zh.i18n.json b/packages/rocketchat-livechat/.app/i18n/zh.i18n.json similarity index 100% rename from packages/rocketchat-livechat/app/i18n/zh.i18n.json rename to packages/rocketchat-livechat/.app/i18n/zh.i18n.json diff --git a/packages/rocketchat-livechat/app/client/lib/_visitor.js b/packages/rocketchat-livechat/.app/imports/client/visitor.js similarity index 56% rename from packages/rocketchat-livechat/app/client/lib/_visitor.js rename to packages/rocketchat-livechat/.app/imports/client/visitor.js index 290ddf92b45a..bb0613cb92e0 100644 --- a/packages/rocketchat-livechat/app/client/lib/_visitor.js +++ b/packages/rocketchat-livechat/.app/imports/client/visitor.js @@ -1,13 +1,14 @@ /* globals Commands */ const msgStream = new Meteor.Streamer('room-messages'); -this.visitor = new class { - constructor() { - this.token = new ReactiveVar(null); - this.room = new ReactiveVar(null); - this.roomToSubscribe = new ReactiveVar(null); - this.roomSubscribed = null; - } +export default { + id: new ReactiveVar(null), + token: new ReactiveVar(null), + room: new ReactiveVar(null), + data: new ReactiveVar(null), + roomToSubscribe: new ReactiveVar(null), + roomSubscribed: null, + connected: null, register() { if (!localStorage.getItem('visitorToken')) { @@ -15,15 +16,31 @@ this.visitor = new class { } this.token.set(localStorage.getItem('visitorToken')); - } + }, + + getId() { + return this.id.get(); + }, + + setId(id) { + return this.id.set(id); + }, + + getData() { + return this.data.get(); + }, + + setData(data) { + this.data.set(data); + }, getToken() { return this.token.get(); - } + }, setRoom(rid) { this.room.set(rid); - } + }, getRoom(createOnEmpty = false) { let roomId = this.room.get(); @@ -33,11 +50,11 @@ this.visitor = new class { } return roomId; - } + }, isSubscribed(roomId) { return this.roomSubscribed === roomId; - } + }, subscribeToRoom(roomId) { if (this.roomSubscribed && this.roomSubscribed === roomId) { @@ -46,7 +63,7 @@ this.visitor = new class { this.roomSubscribed = roomId; - msgStream.on(roomId, (msg) => { + msgStream.on(roomId, { token: this.getToken() }, (msg) => { if (msg.t === 'command') { Commands[msg.msg] && Commands[msg.msg](); } else if (msg.t !== 'livechat_video_call') { @@ -57,13 +74,26 @@ this.visitor = new class { } // notification sound - if (Session.equals('sound', true) && msg.u._id !== Meteor.userId()) { - const audioVolume = RocketChat.getUserPreference(Meteor.user(), 'notificationsSoundVolume'); + if (Session.equals('sound', true) && msg.u._id !== this.getId()) { const audio = document.getElementById('chatAudioNotification'); - audio.volume = Number((audioVolume/100).toPrecision(2)); audio.play(); } } }); + }, + + setConnected() { + if (this.connected) { + return; + } + const token = this.getToken(); + + this.connected = true; + Meteor.call('UserPresence:connect', token, { visitor: token }); + + Meteor.startup(function() { + UserPresence.awayTime = 300000; // 5 minutes + UserPresence.start(token); + }); } }; diff --git a/packages/rocketchat-livechat/.app/package-lock.json b/packages/rocketchat-livechat/.app/package-lock.json new file mode 100644 index 000000000000..2eda1637c29e --- /dev/null +++ b/packages/rocketchat-livechat/.app/package-lock.json @@ -0,0 +1,750 @@ +{ + "name": "rocketchat-livechat", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "abbrev": { + "version": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha1-+PLIh60Qv2f2NPAFtph/7TF5qsg=" + }, + "ajv": { + "version": "https://registry.npmjs.org/ajv/-/ajv-5.5.0.tgz", + "integrity": "sha1-6yhAdG6dxIvV4GOjbj/UAMXqtak=", + "requires": { + "co": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "fast-deep-equal": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", + "fast-json-stable-stringify": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "json-schema-traverse": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz" + } + }, + "ansi-regex": { + "version": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "aproba": { + "version": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha1-aALmJk79GMeQobDVF/DyYnvyyUo=" + }, + "are-we-there-yet": { + "version": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz", + "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=", + "requires": { + "delegates": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "readable-stream": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz" + } + }, + "asn1": { + "version": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" + }, + "assert-plus": { + "version": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "asynckit": { + "version": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "autolinker": { + "version": "https://registry.npmjs.org/autolinker/-/autolinker-1.6.0.tgz", + "integrity": "sha1-utN2t62OQV8i8QL8Dzf2QOZPHL8=" + }, + "aws-sign2": { + "version": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + }, + "aws4": { + "version": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", + "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=" + }, + "babel-runtime": { + "version": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "requires": { + "core-js": "https://registry.npmjs.org/core-js/-/core-js-2.5.1.tgz", + "regenerator-runtime": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz" + } + }, + "balanced-match": { + "version": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "bcrypt": { + "version": "https://registry.npmjs.org/bcrypt/-/bcrypt-1.0.3.tgz", + "integrity": "sha1-sC3cbAtS6ha40883XVoy54DatUg=", + "requires": { + "nan": "https://registry.npmjs.org/nan/-/nan-2.6.2.tgz", + "node-pre-gyp": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.36.tgz" + } + }, + "bcrypt-pbkdf": { + "version": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", + "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", + "optional": true, + "requires": { + "tweetnacl": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz" + } + }, + "block-stream": { + "version": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", + "requires": { + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + } + }, + "boom": { + "version": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", + "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", + "requires": { + "hoek": "https://registry.npmjs.org/hoek/-/hoek-4.2.0.tgz" + } + }, + "brace-expansion": { + "version": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", + "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", + "requires": { + "balanced-match": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "concat-map": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + } + }, + "caseless": { + "version": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "co": { + "version": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" + }, + "code-point-at": { + "version": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, + "combined-stream": { + "version": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", + "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", + "requires": { + "delayed-stream": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" + } + }, + "concat-map": { + "version": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "console-control-strings": { + "version": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" + }, + "core-js": { + "version": "https://registry.npmjs.org/core-js/-/core-js-2.5.1.tgz", + "integrity": "sha1-rmh03GaTd4m4B1T/VCjfZoGcpQs=" + }, + "core-util-is": { + "version": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cryptiles": { + "version": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", + "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", + "requires": { + "boom": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz" + }, + "dependencies": { + "boom": { + "version": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", + "integrity": "sha1-XdnabuOl8wIHdDYpDLcX0/SlTgI=", + "requires": { + "hoek": "https://registry.npmjs.org/hoek/-/hoek-4.2.0.tgz" + } + } + } + }, + "dashdash": { + "version": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" + } + }, + "debug": { + "version": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", + "requires": { + "ms": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" + } + }, + "deep-extend": { + "version": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz", + "integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8=" + }, + "delayed-stream": { + "version": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "delegates": { + "version": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" + }, + "ecc-jsbn": { + "version": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", + "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "optional": true, + "requires": { + "jsbn": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz" + } + }, + "extend": { + "version": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", + "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" + }, + "extsprintf": { + "version": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "fast-deep-equal": { + "version": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", + "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=" + }, + "fast-json-stable-stringify": { + "version": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + }, + "forever-agent": { + "version": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "https://registry.npmjs.org/form-data/-/form-data-2.3.1.tgz", + "integrity": "sha1-b7lPvXGIUwbXPRXMSX/kzE7NRL8=", + "requires": { + "asynckit": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "combined-stream": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", + "mime-types": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz" + } + }, + "fs.realpath": { + "version": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "fstream": { + "version": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", + "requires": { + "graceful-fs": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "rimraf": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz" + } + }, + "fstream-ignore": { + "version": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-1.0.5.tgz", + "integrity": "sha1-nDHa40dnAY/h0kmyTa2mfQktoQU=", + "requires": { + "fstream": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "minimatch": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz" + } + }, + "gauge": { + "version": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "requires": { + "aproba": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "console-control-strings": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "has-unicode": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "object-assign": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "signal-exit": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "string-width": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "strip-ansi": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "wide-align": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz" + } + }, + "getpass": { + "version": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" + } + }, + "glob": { + "version": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=", + "requires": { + "fs.realpath": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "inflight": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "minimatch": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "once": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "path-is-absolute": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" + } + }, + "graceful-fs": { + "version": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" + }, + "har-schema": { + "version": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "har-validator": { + "version": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", + "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", + "requires": { + "ajv": "https://registry.npmjs.org/ajv/-/ajv-5.5.0.tgz", + "har-schema": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz" + } + }, + "has-unicode": { + "version": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" + }, + "hawk": { + "version": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", + "integrity": "sha1-r02RTrBl+bXOTZ0RwcshJu7MMDg=", + "requires": { + "boom": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", + "cryptiles": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", + "hoek": "https://registry.npmjs.org/hoek/-/hoek-4.2.0.tgz", + "sntp": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz" + } + }, + "hoek": { + "version": "https://registry.npmjs.org/hoek/-/hoek-4.2.0.tgz", + "integrity": "sha1-ctnQdU9/4lyi0BrY+PmpRJqJUm0=" + }, + "http-signature": { + "version": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "jsprim": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "sshpk": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz" + } + }, + "inflight": { + "version": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "wrappy": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + } + }, + "inherits": { + "version": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ini": { + "version": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha1-7uJfVtscnsYIXgwid4CD9Zar+Sc=" + }, + "is-fullwidth-code-point": { + "version": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz" + } + }, + "is-typedarray": { + "version": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "isarray": { + "version": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isstream": { + "version": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "jquery": { + "version": "https://registry.npmjs.org/jquery/-/jquery-3.2.1.tgz", + "integrity": "sha1-XE2d5lKvbNCncBVKYxu6ErAVx4c=" + }, + "jsbn": { + "version": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "optional": true + }, + "json-schema": { + "version": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-schema-traverse": { + "version": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" + }, + "json-stringify-safe": { + "version": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "jsprim": { + "version": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "extsprintf": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "json-schema": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "verror": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz" + } + }, + "mime-db": { + "version": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", + "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=" + }, + "mime-types": { + "version": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", + "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", + "requires": { + "mime-db": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz" + } + }, + "minimatch": { + "version": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", + "requires": { + "brace-expansion": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz" + } + }, + "minimist": { + "version": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "mkdirp": { + "version": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" + } + }, + "moment": { + "version": "https://registry.npmjs.org/moment/-/moment-2.19.3.tgz", + "integrity": "sha1-vbmdJw1tf9p4zA+6zoVeJ/59pp8=" + }, + "ms": { + "version": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "nan": { + "version": "https://registry.npmjs.org/nan/-/nan-2.6.2.tgz", + "integrity": "sha1-5P805slf37WuzAjeZZb0NgWn20U=" + }, + "node-pre-gyp": { + "version": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.36.tgz", + "integrity": "sha1-22BBEst04NR3VU6bUFsXq936t4Y=", + "requires": { + "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "nopt": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "npmlog": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "rc": "https://registry.npmjs.org/rc/-/rc-1.2.2.tgz", + "request": "https://registry.npmjs.org/request/-/request-2.83.0.tgz", + "rimraf": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "semver": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", + "tar": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "tar-pack": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.4.1.tgz" + } + }, + "nopt": { + "version": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", + "requires": { + "abbrev": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "osenv": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz" + } + }, + "npmlog": { + "version": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha1-CKfyqL9zRgR3mp76StXMcXq7lUs=", + "requires": { + "are-we-there-yet": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz", + "console-control-strings": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "gauge": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "set-blocking": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" + } + }, + "number-is-nan": { + "version": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "oauth-sign": { + "version": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" + }, + "object-assign": { + "version": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "once": { + "version": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + } + }, + "os-homedir": { + "version": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" + }, + "os-tmpdir": { + "version": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + }, + "osenv": { + "version": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz", + "integrity": "sha1-Qv5tWVPfBsgGS+bxdsPQWqqjRkQ=", + "requires": { + "os-homedir": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "os-tmpdir": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" + } + }, + "path-is-absolute": { + "version": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "performance-now": { + "version": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "process-nextick-args": { + "version": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" + }, + "punycode": { + "version": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "qs": { + "version": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", + "integrity": "sha1-NJzfbu+J7EXBLX1es/wMhwNDptg=" + }, + "rc": { + "version": "https://registry.npmjs.org/rc/-/rc-1.2.2.tgz", + "integrity": "sha1-2M6ctX6NZNnHut2YdsfDTL48cHc=", + "requires": { + "deep-extend": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz", + "ini": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "minimist": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "strip-json-comments": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz" + }, + "dependencies": { + "minimist": { + "version": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + } + } + }, + "readable-stream": { + "version": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha1-No8lEtefnUb9/HE0mueHi7weuVw=", + "requires": { + "core-util-is": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "isarray": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "process-nextick-args": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "string_decoder": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "util-deprecate": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + } + }, + "regenerator-runtime": { + "version": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz", + "integrity": "sha1-flT+W1zNXWYk6mJVw0c74JC4AuE=" + }, + "request": { + "version": "https://registry.npmjs.org/request/-/request-2.83.0.tgz", + "integrity": "sha1-ygtl2gLtYpNYh4COb1EDgQNOM1Y=", + "requires": { + "aws-sign2": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "aws4": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", + "caseless": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "combined-stream": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", + "extend": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", + "forever-agent": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "form-data": "https://registry.npmjs.org/form-data/-/form-data-2.3.1.tgz", + "har-validator": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", + "hawk": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", + "http-signature": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "is-typedarray": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "isstream": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "json-stringify-safe": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "mime-types": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", + "oauth-sign": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "performance-now": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "qs": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", + "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "stringstream": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", + "tough-cookie": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", + "tunnel-agent": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "uuid": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz" + } + }, + "rimraf": { + "version": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha1-LtgVDSShbqhlHm1u8PR8QVjOejY=", + "requires": { + "glob": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz" + } + }, + "safe-buffer": { + "version": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha1-iTMSr2myEj3vcfV4iQAWce6yyFM=" + }, + "semver": { + "version": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", + "integrity": "sha1-4FnAnYVx8FQII3M0M1BdOi8AsY4=" + }, + "set-blocking": { + "version": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "signal-exit": { + "version": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + }, + "sntp": { + "version": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz", + "integrity": "sha1-LGzsFP7cIiJznK+bXD2F0cxaLMg=", + "requires": { + "hoek": "https://registry.npmjs.org/hoek/-/hoek-4.2.0.tgz" + } + }, + "sprintf-js": { + "version": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.1.tgz", + "integrity": "sha1-Nr54Mgr+WAH2zqPueLblqrlA6gw=" + }, + "sshpk": { + "version": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", + "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", + "requires": { + "asn1": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "assert-plus": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "bcrypt-pbkdf": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", + "dashdash": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "ecc-jsbn": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", + "getpass": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "jsbn": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "tweetnacl": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz" + } + }, + "string-width": { + "version": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "is-fullwidth-code-point": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "strip-ansi": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz" + } + }, + "string_decoder": { + "version": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=", + "requires": { + "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz" + } + }, + "stringstream": { + "version": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", + "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=" + }, + "strip-ansi": { + "version": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz" + } + }, + "strip-json-comments": { + "version": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" + }, + "tar": { + "version": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", + "requires": { + "block-stream": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "fstream": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "inherits": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + } + }, + "tar-pack": { + "version": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.4.1.tgz", + "integrity": "sha1-4dvAOpudO6B+iWrQJzF+tnmhCh8=", + "requires": { + "debug": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "fstream": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "fstream-ignore": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-1.0.5.tgz", + "once": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "readable-stream": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "rimraf": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "tar": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "uid-number": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz" + } + }, + "toastr": { + "version": "https://registry.npmjs.org/toastr/-/toastr-2.1.2.tgz", + "integrity": "sha1-/WkGaudXilszV3JfycfDNem2gd8=" + }, + "tough-cookie": { + "version": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", + "integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=", + "requires": { + "punycode": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz" + } + }, + "tunnel-agent": { + "version": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz" + } + }, + "tweetnacl": { + "version": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "optional": true + }, + "uid-number": { + "version": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz", + "integrity": "sha1-DqEOgDXo61uOREnwbaHHMGY7qoE=" + }, + "underscore": { + "version": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" + }, + "underscore.string": { + "version": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.4.tgz", + "integrity": "sha1-LCo/n4PmR2L9xF5s6sZRQoZCE9s=", + "requires": { + "sprintf-js": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.1.tgz", + "util-deprecate": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + } + }, + "util-deprecate": { + "version": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "uuid": { + "version": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", + "integrity": "sha1-PdPT55Crwk17DToDT/q6vijrvAQ=" + }, + "verror": { + "version": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "core-util-is": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "extsprintf": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz" + } + }, + "wide-align": { + "version": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz", + "integrity": "sha1-Vx4PGwYEY268DfwhsDObvjE0FxA=", + "requires": { + "string-width": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz" + } + }, + "wrappy": { + "version": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + } + } +} diff --git a/packages/rocketchat-livechat/app/package.json b/packages/rocketchat-livechat/.app/package.json similarity index 100% rename from packages/rocketchat-livechat/app/package.json rename to packages/rocketchat-livechat/.app/package.json diff --git a/packages/rocketchat-livechat/app/run.sh b/packages/rocketchat-livechat/.app/run.sh similarity index 100% rename from packages/rocketchat-livechat/app/run.sh rename to packages/rocketchat-livechat/.app/run.sh diff --git a/packages/rocketchat-livechat/app/client/startup/visitor.js b/packages/rocketchat-livechat/app/client/startup/visitor.js deleted file mode 100644 index bd27d17e99af..000000000000 --- a/packages/rocketchat-livechat/app/client/startup/visitor.js +++ /dev/null @@ -1,21 +0,0 @@ -this.visitorId = new ReactiveVar(null); - -Meteor.startup(() => { - if (!localStorage.getItem('rocketChatLivechat')) { - localStorage.setItem('rocketChatLivechat', Random.id()); - } else { - Tracker.autorun(c => { - if (!Meteor.userId() && visitor.getToken()) { - Meteor.call('livechat:loginByToken', visitor.getToken(), (err, result) => { - if (result && result.token) { - Meteor.loginWithToken(result.token, () => { - c.stop(); - }); - } - }); - } - }); - } - - this.visitorId.set(localStorage.getItem('rocketChatLivechat')); -}); diff --git a/packages/rocketchat-livechat/client/collections/LivechatVisitor.js b/packages/rocketchat-livechat/client/collections/LivechatVisitor.js new file mode 100644 index 000000000000..e2b44ef3f14f --- /dev/null +++ b/packages/rocketchat-livechat/client/collections/LivechatVisitor.js @@ -0,0 +1 @@ +this.LivechatVisitor = new Mongo.Collection('rocketchat_livechat_visitor'); diff --git a/packages/rocketchat-livechat/client/views/app/tabbar/visitorForward.js b/packages/rocketchat-livechat/client/views/app/tabbar/visitorForward.js index 97afe34d0a99..58331022a08f 100644 --- a/packages/rocketchat-livechat/client/views/app/tabbar/visitorForward.js +++ b/packages/rocketchat-livechat/client/views/app/tabbar/visitorForward.js @@ -68,7 +68,7 @@ Template.visitorForward.events({ }, 'change #forwardUser, blur #forwardUser'(event, instance) { - if (event.currentTarget.value) { + if (event.currentTarget.value && instance.find('#forwardDepartment')) { instance.find('#forwardDepartment').value = ''; } }, diff --git a/packages/rocketchat-livechat/client/views/app/tabbar/visitorInfo.js b/packages/rocketchat-livechat/client/views/app/tabbar/visitorInfo.js index e23925b2a91c..d7c745ef730b 100644 --- a/packages/rocketchat-livechat/client/views/app/tabbar/visitorInfo.js +++ b/packages/rocketchat-livechat/client/views/app/tabbar/visitorInfo.js @@ -1,3 +1,5 @@ +/* globals LivechatVisitor */ + import _ from 'underscore'; import s from 'underscore.string'; import moment from 'moment'; @@ -243,6 +245,6 @@ Template.visitorInfo.onCreated(function() { } this.autorun(() => { - this.user.set(Meteor.users.findOne({ '_id': this.visitorId.get() })); + this.user.set(LivechatVisitor.findOne({ '_id': this.visitorId.get() })); }); }); diff --git a/packages/rocketchat-livechat/config.js b/packages/rocketchat-livechat/config.js index 2d0500d738d8..b404935af44e 100644 --- a/packages/rocketchat-livechat/config.js +++ b/packages/rocketchat-livechat/config.js @@ -131,6 +131,27 @@ Meteor.startup(function() { i18nLabel: 'Send_request_on_offline_messages' }); + RocketChat.settings.add('Livechat_webhook_on_capture', false, { + type: 'boolean', + group: 'Livechat', + section: 'CRM_Integration', + i18nLabel: 'Send_request_on_lead_capture' + }); + + RocketChat.settings.add('Livechat_lead_email_regex', '\\b[A-Z0-9._%+-]+@(?:[A-Z0-9-]+\\.)+[A-Z]{2,4}\\b', { + type: 'string', + group: 'Livechat', + section: 'CRM_Integration', + i18nLabel: 'Lead_capture_email_regex' + }); + + RocketChat.settings.add('Livechat_lead_phone_regex', '((?:\\([0-9]{1,3}\\)|[0-9]{2})[ \\-]*?[0-9]{4,5}(?:[\\-\\s\\_]{1,2})?[0-9]{4}(?:(?=[^0-9])|$)|[0-9]{4,5}(?:[\\-\\s\\_]{1,2})?[0-9]{4}(?:(?=[^0-9])|$))', { + type: 'string', + group: 'Livechat', + section: 'CRM_Integration', + i18nLabel: 'Lead_capture_phone_regex' + }); + RocketChat.settings.add('Livechat_Knowledge_Enabled', false, { type: 'boolean', group: 'Livechat', diff --git a/packages/rocketchat-livechat/imports/server/rest/sms.js b/packages/rocketchat-livechat/imports/server/rest/sms.js index c01a1b34fb06..56ca236c3b5b 100644 --- a/packages/rocketchat-livechat/imports/server/rest/sms.js +++ b/packages/rocketchat-livechat/imports/server/rest/sms.js @@ -1,10 +1,12 @@ +import LivechatVisitors from '../../../server/models/LivechatVisitors'; + RocketChat.API.v1.addRoute('livechat/sms-incoming/:service', { post() { const SMSService = RocketChat.SMS.getService(this.urlParams.service); const sms = SMSService.parse(this.bodyParams); - let visitor = RocketChat.models.Users.findOneVisitorByPhone(sms.from); + let visitor = LivechatVisitors.findOneVisitorByPhone(sms.from); const sendMessage = { message: { @@ -18,19 +20,19 @@ RocketChat.API.v1.addRoute('livechat/sms-incoming/:service', { }; if (visitor) { - const rooms = RocketChat.models.Rooms.findOpenByVisitorToken(visitor.profile.token).fetch(); + const rooms = RocketChat.models.Rooms.findOpenByVisitorToken(visitor.token).fetch(); if (rooms && rooms.length > 0) { sendMessage.message.rid = rooms[0]._id; } else { sendMessage.message.rid = Random.id(); } - sendMessage.message.token = visitor.profile.token; + sendMessage.message.token = visitor.token; } else { sendMessage.message.rid = Random.id(); sendMessage.message.token = Random.id(); - const userId = RocketChat.Livechat.registerGuest({ + const visitorId = RocketChat.Livechat.registerGuest({ username: sms.from.replace(/[^0-9]/g, ''), token: sendMessage.message.token, phone: { @@ -38,7 +40,7 @@ RocketChat.API.v1.addRoute('livechat/sms-incoming/:service', { } }); - visitor = RocketChat.models.Users.findOneById(userId); + visitor = LivechatVisitors.findOneById(visitorId); } sendMessage.message.msg = sms.body; diff --git a/packages/rocketchat-livechat/package.js b/packages/rocketchat-livechat/package.js index f6312b1e8cb0..ea9bbad4675e 100644 --- a/packages/rocketchat-livechat/package.js +++ b/packages/rocketchat-livechat/package.js @@ -44,6 +44,7 @@ Package.onUse(function(api) { api.addFiles('livechat.js', 'server'); api.addFiles('server/startup.js', 'server'); + api.addFiles('server/visitorStatus.js', 'server'); api.addFiles('permissions.js', 'server'); api.addFiles('messageTypes.js'); api.addFiles('roomType.js'); @@ -66,6 +67,7 @@ Package.onUse(function(api) { api.addFiles('client/collections/LivechatTrigger.js', 'client'); api.addFiles('client/collections/LivechatInquiry.js', 'client'); api.addFiles('client/collections/livechatOfficeHour.js', 'client'); + api.addFiles('client/collections/LivechatVisitor.js', 'client'); api.addFiles('client/methods/changeLivechatStatus.js', 'client'); @@ -129,6 +131,7 @@ Package.onUse(function(api) { // hooks api.addFiles('server/hooks/externalMessage.js', 'server'); + api.addFiles('server/hooks/leadCapture.js', 'server'); api.addFiles('server/hooks/markRoomResponded.js', 'server'); api.addFiles('server/hooks/offlineMessage.js', 'server'); api.addFiles('server/hooks/RDStation.js', 'server'); @@ -145,6 +148,7 @@ Package.onUse(function(api) { api.addFiles('server/methods/getCustomFields.js', 'server'); api.addFiles('server/methods/getAgentData.js', 'server'); api.addFiles('server/methods/getInitialData.js', 'server'); + api.addFiles('server/methods/loadHistory.js', 'server'); api.addFiles('server/methods/loginByToken.js', 'server'); api.addFiles('server/methods/pageVisited.js', 'server'); api.addFiles('server/methods/registerGuest.js', 'server'); diff --git a/packages/rocketchat-livechat/plugin/build.bat b/packages/rocketchat-livechat/plugin/build.bat index d586b8120482..bf51ac5c687b 100644 --- a/packages/rocketchat-livechat/plugin/build.bat +++ b/packages/rocketchat-livechat/plugin/build.bat @@ -1,7 +1,7 @@ @echo off SET NODE_ENV="production" -cd packages/rocketchat-livechat/app +cd packages/rocketchat-livechat/.app call meteor npm install --production call meteor build --headless --directory .meteor/build/ diff --git a/packages/rocketchat-livechat/plugin/build.sh b/packages/rocketchat-livechat/plugin/build.sh index 1039e6a20717..f44175f215b0 100644 --- a/packages/rocketchat-livechat/plugin/build.sh +++ b/packages/rocketchat-livechat/plugin/build.sh @@ -3,7 +3,7 @@ export LIVECHAT_DIR="../../../public/livechat" export BUILD_DIR="../build" export BUNDLE_DIR="../build/bundle/programs/web.browser" -cd packages/rocketchat-livechat/app +cd packages/rocketchat-livechat/.app meteor npm install --production meteor build --headless --directory $BUILD_DIR diff --git a/packages/rocketchat-livechat/roomType.js b/packages/rocketchat-livechat/roomType.js index 19d4d1650f17..5a012f7edf5d 100644 --- a/packages/rocketchat-livechat/roomType.js +++ b/packages/rocketchat-livechat/roomType.js @@ -57,19 +57,13 @@ class LivechatRoomType extends RoomTypeConfig { } getUserStatus(roomId) { - let guestName; const room = Session.get(`roomData${ roomId }`); - if (room) { - guestName = room.v && room.v.username; - } else { - const inquiry = LivechatInquiry.findOne({rid: roomId}); - guestName = inquiry && inquiry.v && inquiry.v.username; + return room.v && room.v.status; } - if (guestName) { - return Session.get(`user_${ guestName }_status`); - } + const inquiry = LivechatInquiry.findOne({ rid: roomId }); + return inquiry && inquiry.v && inquiry.v.status; } allowRoomSettingChange(room, setting) { diff --git a/packages/rocketchat-livechat/server/hooks/leadCapture.js b/packages/rocketchat-livechat/server/hooks/leadCapture.js new file mode 100644 index 000000000000..0d3cc63e66bf --- /dev/null +++ b/packages/rocketchat-livechat/server/hooks/leadCapture.js @@ -0,0 +1,53 @@ +import LivechatVisitors from '../../server/models/LivechatVisitors'; + +function validateMessage(message, room) { + // skips this callback if the message was edited + if (message.editedAt) { + return false; + } + + if (!RocketChat.settings.get('Livechat_Facebook_Enabled')) { + return false; + } + + // only send the sms by SMS if it is a livechat room with SMS set to true + if (!(typeof room.t !== 'undefined' && room.t === 'l' && room.v && room.v.token)) { + return false; + } + + // if the message hasn't a token, it was NOT sent from the visitor, so ignore it + if (!message.token) { + return false; + } + + // if the message has a type means it is a special message (like the closing comment), so skips + if (message.t) { + return false; + } + + return true; +} + +RocketChat.callbacks.add('afterSaveMessage', function(message, room) { + if (!RocketChat.settings.get('Livechat_webhook_on_capture')) { + return message; + } + + if (!validateMessage(message, room)) { + return message; + } + + const phoneRegexp = new RegExp(RocketChat.settings.get('Livechat_lead_phone_regex'), 'g'); + const msgPhones = message.msg.match(phoneRegexp); + + const emailRegexp = new RegExp(RocketChat.settings.get('Livechat_lead_email_regex'), 'gi'); + const msgEmails = message.msg.match(emailRegexp); + + if (msgEmails || msgPhones) { + LivechatVisitors.saveGuestEmailPhoneById(room.v._id, msgEmails, msgPhones); + + RocketChat.callbacks.run('livechat.leadCapture', room); + } + + return message; +}, RocketChat.callbacks.priority.LOW, 'leadCapture'); diff --git a/packages/rocketchat-livechat/server/hooks/sendToCRM.js b/packages/rocketchat-livechat/server/hooks/sendToCRM.js index 7a09bbc0fed9..8b2926997a29 100644 --- a/packages/rocketchat-livechat/server/hooks/sendToCRM.js +++ b/packages/rocketchat-livechat/server/hooks/sendToCRM.js @@ -1,37 +1,27 @@ -function sendToCRM(hook, room) { - if (!RocketChat.settings.get('Livechat_webhook_on_close')) { - return room; - } - - // Do not send to CRM if the chat is still open - if (hook === 'saveLivechatInfo' && room.open) { - return room; - } - +function sendToCRM(type, room, includeMessages = true) { const postData = RocketChat.Livechat.getLivechatRoomGuestInfo(room); - if (hook === 'closeRoom') { - postData.type = 'LivechatSession'; - } else if (hook === 'saveLivechatInfo') { - postData.type = 'LivechatEdit'; - } + + postData.type = type; postData.messages = []; - RocketChat.models.Messages.findVisibleByRoomId(room._id, { sort: { ts: 1 } }).forEach((message) => { - if (message.t) { - return; - } - const msg = { - username: message.u.username, - msg: message.msg, - ts: message.ts - }; - - if (message.u.username !== postData.visitor.username) { - msg.agentId = message.u._id; - } - postData.messages.push(msg); - }); + if (includeMessages) { + RocketChat.models.Messages.findVisibleByRoomId(room._id, { sort: { ts: 1 } }).forEach((message) => { + if (message.t) { + return; + } + const msg = { + username: message.u.username, + msg: message.msg, + ts: message.ts + }; + + if (message.u.username !== postData.visitor.username) { + msg.agentId = message.u._id; + } + postData.messages.push(msg); + }); + } const response = RocketChat.Livechat.sendRequest(postData); @@ -43,9 +33,22 @@ function sendToCRM(hook, room) { } RocketChat.callbacks.add('livechat.closeRoom', (room) => { - return sendToCRM('closeRoom', room); + if (!RocketChat.settings.get('Livechat_webhook_on_close')) { + return room; + } + + return sendToCRM('LivechatSession', room); }, RocketChat.callbacks.priority.MEDIUM, 'livechat-send-crm-close-room'); RocketChat.callbacks.add('livechat.saveInfo', (room) => { - return sendToCRM('saveLivechatInfo', room); + // Do not send to CRM if the chat is still open + if (room.open) { + return room; + } + + return sendToCRM('LivechatEdit', room); }, RocketChat.callbacks.priority.MEDIUM, 'livechat-send-crm-save-info'); + +RocketChat.callbacks.add('livechat.leadCapture', (room) => { + return sendToCRM('LeadCapture', room, false); +}, RocketChat.callbacks.priority.MEDIUM, 'livechat-send-crm-lead-capture'); diff --git a/packages/rocketchat-livechat/server/lib/Livechat.js b/packages/rocketchat-livechat/server/lib/Livechat.js index 11d85e9fe84a..2769b7ff2e35 100644 --- a/packages/rocketchat-livechat/server/lib/Livechat.js +++ b/packages/rocketchat-livechat/server/lib/Livechat.js @@ -2,6 +2,7 @@ import _ from 'underscore'; import s from 'underscore.string'; import UAParser from 'ua-parser-js'; +import LivechatVisitors from '../models/LivechatVisitors'; RocketChat.Livechat = { historyMonitorType: 'url', @@ -60,10 +61,9 @@ RocketChat.Livechat = { room = RocketChat.QueueMethods[routingMethod](guest, message, roomInfo); newRoom = true; - } else { - room = Meteor.call('canAccessRoom', message.rid, guest._id); } - if (!room) { + + if (!room || room.v.token !== guest.token) { throw new Meteor.Error('cannot-access-room'); } @@ -78,53 +78,33 @@ RocketChat.Livechat = { // return messages; return _.extend(RocketChat.sendMessage(guest, message, room), { newRoom, showConnecting: this.showConnecting() }); }, - registerGuest({ token, name, email, department, phone, loginToken, username } = {}) { + registerGuest({ token, name, email, department, phone, username } = {}) { check(token, String); let userId; const updateUser = { $set: { - profile: { - guest: true, - token - } + token } }; - const user = RocketChat.models.Users.getVisitorByToken(token, { fields: { _id: 1 } }); + const user = LivechatVisitors.getVisitorByToken(token, { fields: { _id: 1 } }); if (user) { userId = user._id; - if (loginToken) { - if (!updateUser.$addToSet) { - updateUser.$addToSet = {}; - } - updateUser.$addToSet['services.resume.loginTokens'] = loginToken; - } } else { if (!username) { - username = RocketChat.models.Users.getNextVisitorUsername(); + username = LivechatVisitors.getNextVisitorUsername(); } let existingUser = null; - if (s.trim(email) !== '' && (existingUser = RocketChat.models.Users.findOneGuestByEmailAddress(email))) { - if (loginToken) { - if (!updateUser.$addToSet) { - updateUser.$addToSet = {}; - } - updateUser.$addToSet['services.resume.loginTokens'] = loginToken; - } - + if (s.trim(email) !== '' && (existingUser = LivechatVisitors.findOneGuestByEmailAddress(email))) { userId = existingUser._id; } else { - const userData = { username, - globalRoles: ['livechat-guest'], - department, - type: 'visitor', - joinDefaultChannels: false + department }; if (this.connection) { @@ -133,15 +113,7 @@ RocketChat.Livechat = { userData.host = this.connection.httpHeaders.host; } - userId = Accounts.insertUserDoc({}, userData); - - if (loginToken) { - updateUser.$set.services = { - resume: { - loginTokens: [ loginToken ] - } - }; - } + userId = LivechatVisitors.insert(userData); } } @@ -158,10 +130,10 @@ RocketChat.Livechat = { } if (name) { - RocketChat._setRealName(userId, name); + updateUser.$set.name = name; } - Meteor.users.update(userId, updateUser); + LivechatVisitors.updateById(userId, updateUser); return userId; }, @@ -174,7 +146,7 @@ RocketChat.Livechat = { } }; - const user = RocketChat.models.Users.getVisitorByToken(token, { fields: { _id: 1 } }); + const user = LivechatVisitors.getVisitorByToken(token, { fields: { _id: 1 } }); if (user) { return Meteor.users.update(user._id, updateUser); } @@ -192,7 +164,7 @@ RocketChat.Livechat = { if (phone) { updateData.phone = phone; } - const ret = RocketChat.models.Users.saveGuestById(_id, updateData); + const ret = LivechatVisitors.saveGuestById(_id, updateData); Meteor.defer(() => { RocketChat.callbacks.run('livechat.saveGuest', updateData); @@ -201,16 +173,29 @@ RocketChat.Livechat = { return ret; }, - closeRoom({ user, room, comment }) { + closeRoom({ user, visitor, room, comment }) { const now = new Date(); - RocketChat.models.Rooms.closeByRoomId(room._id, { - user: { - _id: user._id, - username: user.username - }, + + const closeData = { closedAt: now, chatDuration: (now.getTime() - room.ts) / 1000 - }); + }; + + if (user) { + closeData.closer = 'user'; + closeData.closedBy = { + _id: user._id, + username: user.username + }; + } else if (visitor) { + closeData.closer = 'visitor'; + closeData.closedBy = { + _id: visitor._id, + username: visitor.username + }; + } + + RocketChat.models.Rooms.closeByRoomId(room._id, closeData); const message = { t: 'livechat-close', @@ -220,8 +205,8 @@ RocketChat.Livechat = { RocketChat.sendMessage(user, message, room); - RocketChat.models.Subscriptions.hideByRoomIdAndUserId(room._id, user._id); - RocketChat.models.Messages.createCommandWithRoomIdAndUser('promptTranscript', room._id, user); + RocketChat.models.Subscriptions.hideByRoomIdAndUserId(room._id, room.servedBy._id); + RocketChat.models.Messages.createCommandWithRoomIdAndUser('promptTranscript', room._id, room.servedBy); Meteor.defer(() => { RocketChat.callbacks.run('livechat.closeRoom', room); @@ -372,7 +357,7 @@ RocketChat.Livechat = { }, getLivechatRoomGuestInfo(room) { - const visitor = RocketChat.models.Users.findOneById(room.v._id); + const visitor = LivechatVisitors.findOneById(room.v._id); const agent = RocketChat.models.Users.findOneById(room.servedBy._id); const ua = new UAParser(); @@ -412,14 +397,14 @@ RocketChat.Livechat = { } if (visitor.visitorEmails && visitor.visitorEmails.length > 0) { - postData.visitor.email = visitor.visitorEmails[0].address; + postData.visitor.email = visitor.visitorEmails; } if (visitor.phone && visitor.phone.length > 0) { - postData.visitor.phone = visitor.phone[0].phoneNumber; + postData.visitor.phone = visitor.phone; } if (agent.emails && agent.emails.length > 0) { - postData.agent.email = agent.emails[0].address; + postData.agent.email = agent.emails; } return postData; @@ -538,7 +523,18 @@ RocketChat.Livechat = { }; RocketChat.Livechat.stream = new Meteor.Streamer('livechat-room'); -RocketChat.Livechat.stream.allowRead('logged'); + +RocketChat.Livechat.stream.allowRead((roomId, extraData) => { + const room = RocketChat.models.Rooms.findOneById(roomId); + if (!room) { + console.warn(`Invalid eventName: "${ roomId }"`); + return false; + } + if (room.t === 'l' && extraData && extraData.token && room.v.token === extraData.token) { + return true; + } + return false; +}); RocketChat.settings.get('Livechat_history_monitor_type', (key, value) => { RocketChat.Livechat.historyMonitorType = value; diff --git a/packages/rocketchat-livechat/server/lib/QueueMethods.js b/packages/rocketchat-livechat/server/lib/QueueMethods.js index 318ce9918f76..d882ee4f98fb 100644 --- a/packages/rocketchat-livechat/server/lib/QueueMethods.js +++ b/packages/rocketchat-livechat/server/lib/QueueMethods.js @@ -26,7 +26,8 @@ RocketChat.QueueMethods = { v: { _id: guest._id, username: guest.username, - token: message.token + token: message.token, + status: guest.status || 'online' }, servedBy: { _id: agent.agentId, @@ -109,7 +110,8 @@ RocketChat.QueueMethods = { v: { _id: guest._id, username: guest.username, - token: message.token + token: message.token, + status: guest.status || 'online' }, t: 'l' }; @@ -125,7 +127,8 @@ RocketChat.QueueMethods = { v: { _id: guest._id, username: guest.username, - token: message.token + token: message.token, + status: guest.status }, cl: false, open: true, diff --git a/packages/rocketchat-livechat/server/methods/closeByVisitor.js b/packages/rocketchat-livechat/server/methods/closeByVisitor.js index 4aa20f1a1c8c..c6c54596c53d 100644 --- a/packages/rocketchat-livechat/server/methods/closeByVisitor.js +++ b/packages/rocketchat-livechat/server/methods/closeByVisitor.js @@ -1,21 +1,19 @@ -Meteor.methods({ - 'livechat:closeByVisitor'(roomId) { - if (!Meteor.userId()) { - throw new Meteor.Error('error-not-authorized', 'Not authorized', { method: 'livechat:closeByVisitor' }); - } +import LivechatVisitors from '../models/LivechatVisitors'; - const room = RocketChat.models.Rooms.findOneOpenByVisitorId(Meteor.userId(), roomId); +Meteor.methods({ + 'livechat:closeByVisitor'({ roomId, token }) { + const room = RocketChat.models.Rooms.findOneOpenByVisitorToken(token, roomId); if (!room || !room.open) { return false; } - const user = Meteor.user(); + const visitor = LivechatVisitors.getVisitorByToken(token); - const language = (user && user.language) || RocketChat.settings.get('language') || 'en'; + const language = (visitor && visitor.language) || RocketChat.settings.get('language') || 'en'; return RocketChat.Livechat.closeRoom({ - user, + visitor, room, comment: TAPi18n.__('Closed_by_visitor', { lng: language }) }); diff --git a/packages/rocketchat-livechat/server/methods/getAgentData.js b/packages/rocketchat-livechat/server/methods/getAgentData.js index 98f8e2808ae8..75a6981abb51 100644 --- a/packages/rocketchat-livechat/server/methods/getAgentData.js +++ b/packages/rocketchat-livechat/server/methods/getAgentData.js @@ -1,12 +1,15 @@ +import LivechatVisitors from '../models/LivechatVisitors'; + Meteor.methods({ - 'livechat:getAgentData'(roomId) { + 'livechat:getAgentData'({ roomId, token }) { check(roomId, String); + check(token, String); const room = RocketChat.models.Rooms.findOneById(roomId); - const user = Meteor.user(); + const visitor = LivechatVisitors.getVisitorByToken(token); // allow to only user to send transcripts from their own chats - if (!room || room.t !== 'l' || !room.v || !user.profile || room.v.token !== user.profile.token) { + if (!room || room.t !== 'l' || !room.v || room.v.token !== visitor.token) { throw new Meteor.Error('error-invalid-room', 'Invalid room'); } diff --git a/packages/rocketchat-livechat/server/methods/getInitialData.js b/packages/rocketchat-livechat/server/methods/getInitialData.js index 26e99aef2ffa..bca2a9787764 100644 --- a/packages/rocketchat-livechat/server/methods/getInitialData.js +++ b/packages/rocketchat-livechat/server/methods/getInitialData.js @@ -1,5 +1,7 @@ import _ from 'underscore'; +import LivechatVisitors from '../models/LivechatVisitors'; + Meteor.methods({ 'livechat:getInitialData'(visitorToken) { const info = { @@ -8,6 +10,7 @@ Meteor.methods({ color: null, registrationForm: null, room: null, + visitor: null, triggers: [], departments: [], allowSwitchingDepartments: null, @@ -36,6 +39,18 @@ Meteor.methods({ info.room = room[0]; } + const visitor = LivechatVisitors.getVisitorByToken(visitorToken, { + fields: { + name: 1, + username: 1, + visitorEmails: 1 + } + }); + + if (room) { + info.visitor = visitor; + } + const initSettings = RocketChat.Livechat.getInitSettings(); info.title = initSettings.Livechat_title; diff --git a/packages/rocketchat-livechat/server/methods/loadHistory.js b/packages/rocketchat-livechat/server/methods/loadHistory.js new file mode 100644 index 000000000000..fdbab8bcc23c --- /dev/null +++ b/packages/rocketchat-livechat/server/methods/loadHistory.js @@ -0,0 +1,13 @@ +import LivechatVisitors from '../models/LivechatVisitors'; + +Meteor.methods({ + 'livechat:loadHistory'({ token, rid, end, limit = 20, ls}) { + const visitor = LivechatVisitors.getVisitorByToken(token, { fields: { _id: 1 } }); + + if (!visitor) { + return; + } + + return RocketChat.loadMessageHistory({ userId: visitor._id, rid, end, limit, ls }); + } +}); diff --git a/packages/rocketchat-livechat/server/methods/loginByToken.js b/packages/rocketchat-livechat/server/methods/loginByToken.js index 78c08a4d454f..577bceb6c9f7 100644 --- a/packages/rocketchat-livechat/server/methods/loginByToken.js +++ b/packages/rocketchat-livechat/server/methods/loginByToken.js @@ -1,28 +1,15 @@ +import LivechatVisitors from '../models/LivechatVisitors'; + Meteor.methods({ 'livechat:loginByToken'(token) { - const user = RocketChat.models.Users.getVisitorByToken(token, { fields: { _id: 1 } }); + const user = LivechatVisitors.getVisitorByToken(token, { fields: { _id: 1 } }); if (!user) { return; } - const stampedToken = Accounts._generateStampedLoginToken(); - const hashStampedToken = Accounts._hashStampedToken(stampedToken); - - const updateUser = { - $set: { - services: { - resume: { - loginTokens: [ hashStampedToken ] - } - } - } - }; - - Meteor.users.update(user._id, updateUser); - return { - token: stampedToken.token + _id: user._id }; } }); diff --git a/packages/rocketchat-livechat/server/methods/registerGuest.js b/packages/rocketchat-livechat/server/methods/registerGuest.js index 9dcc0b9082cd..5cef4e7c19a4 100644 --- a/packages/rocketchat-livechat/server/methods/registerGuest.js +++ b/packages/rocketchat-livechat/server/methods/registerGuest.js @@ -1,22 +1,17 @@ Meteor.methods({ 'livechat:registerGuest'({ token, name, email, department } = {}) { - const stampedToken = Accounts._generateStampedLoginToken(); - const hashStampedToken = Accounts._hashStampedToken(stampedToken); - const userId = RocketChat.Livechat.registerGuest.call(this, { token, name, email, - department, - loginToken: hashStampedToken + department }); // update visited page history to not expire RocketChat.models.LivechatPageVisited.keepHistoryForToken(token); return { - userId, - token: stampedToken.token + userId }; } }); diff --git a/packages/rocketchat-livechat/server/methods/saveSurveyFeedback.js b/packages/rocketchat-livechat/server/methods/saveSurveyFeedback.js index fb296b9ba249..ee1ec2ebe983 100644 --- a/packages/rocketchat-livechat/server/methods/saveSurveyFeedback.js +++ b/packages/rocketchat-livechat/server/methods/saveSurveyFeedback.js @@ -1,4 +1,5 @@ /* eslint new-cap: [2, {"capIsNewExceptions": ["Match.ObjectIncluding"]}] */ +import LivechatVisitors from '../models/LivechatVisitors'; import _ from 'underscore'; Meteor.methods({ @@ -7,10 +8,10 @@ Meteor.methods({ check(visitorRoom, String); check(formData, [Match.ObjectIncluding({ name: String, value: String })]); - const visitor = RocketChat.models.Users.getVisitorByToken(visitorToken); + const visitor = LivechatVisitors.getVisitorByToken(visitorToken); const room = RocketChat.models.Rooms.findOneById(visitorRoom); - if (visitor !== undefined && room !== undefined && room.v !== undefined && visitor.profile !== undefined && room.v.token === visitor.profile.token) { + if (visitor !== undefined && room !== undefined && room.v !== undefined && room.v.token === visitor.token) { const updateData = {}; for (const item of formData) { if (_.contains(['satisfaction', 'agentKnowledge', 'agentResposiveness', 'agentFriendliness'], item.name) && _.contains(['1', '2', '3', '4', '5'], item.value)) { diff --git a/packages/rocketchat-livechat/server/methods/sendMessageLivechat.js b/packages/rocketchat-livechat/server/methods/sendMessageLivechat.js index b34c80019d98..b112474e4aeb 100644 --- a/packages/rocketchat-livechat/server/methods/sendMessageLivechat.js +++ b/packages/rocketchat-livechat/server/methods/sendMessageLivechat.js @@ -1,16 +1,33 @@ +import LivechatVisitors from '../models/LivechatVisitors'; + Meteor.methods({ - sendMessageLivechat(message) { - check(message.rid, String); - check(message.token, String); + sendMessageLivechat({ token, _id, rid, msg }) { + check(token, String); + check(_id, String); + check(rid, String); + check(msg, String); - const guest = Meteor.users.findOne(Meteor.userId(), { + const guest = LivechatVisitors.getVisitorByToken(token, { fields: { name: 1, username: 1, - department: 1 + department: 1, + token: 1 } }); - return RocketChat.Livechat.sendMessage({ guest, message }); + if (!guest) { + throw new Meteor.Error('invalid-token'); + } + + return RocketChat.Livechat.sendMessage({ + guest, + message: { + _id, + rid, + msg, + token + } + }); } }); diff --git a/packages/rocketchat-livechat/server/methods/sendTranscript.js b/packages/rocketchat-livechat/server/methods/sendTranscript.js index a37a874a9c4f..949642ab232e 100644 --- a/packages/rocketchat-livechat/server/methods/sendTranscript.js +++ b/packages/rocketchat-livechat/server/methods/sendTranscript.js @@ -2,17 +2,20 @@ /* Send a transcript of the room converstation to the given email */ import moment from 'moment'; +import LivechatVisitors from '../models/LivechatVisitors'; + Meteor.methods({ - 'livechat:sendTranscript'(rid, email) { + 'livechat:sendTranscript'(token, rid, email) { check(rid, String); check(email, String); const room = RocketChat.models.Rooms.findOneById(rid); - const user = Meteor.user(); - const userLanguage = user.language || RocketChat.settings.get('language') || 'en'; + + const visitor = LivechatVisitors.getVisitorByToken(token); + const userLanguage = (visitor && visitor.language) || RocketChat.settings.get('language') || 'en'; // allow to only user to send transcripts from their own chats - if (!room || room.t !== 'l' || !room.v || !user.profile || room.v.token !== user.profile.token) { + if (!room || room.t !== 'l' || !room.v || room.v.token !== token) { throw new Meteor.Error('error-invalid-room', 'Invalid room'); } @@ -27,7 +30,7 @@ Meteor.methods({ } let author; - if (message.u._id === Meteor.userId()) { + if (message.u._id === visitor._id) { author = TAPi18n.__('You', { lng: userLanguage }); } else { author = message.u.username; diff --git a/packages/rocketchat-livechat/server/methods/setCustomField.js b/packages/rocketchat-livechat/server/methods/setCustomField.js index 5187cedde36e..1ff16847a5a0 100644 --- a/packages/rocketchat-livechat/server/methods/setCustomField.js +++ b/packages/rocketchat-livechat/server/methods/setCustomField.js @@ -1,3 +1,5 @@ +import LivechatVisitors from '../models/LivechatVisitors'; + Meteor.methods({ 'livechat:setCustomField'(token, key, value, overwrite = true) { const customField = RocketChat.models.LivechatCustomField.findOneById(key); @@ -6,7 +8,7 @@ Meteor.methods({ return RocketChat.models.Rooms.updateLivechatDataByToken(token, key, value, overwrite); } else { // Save in user - return RocketChat.models.Users.updateLivechatDataByToken(token, key, value, overwrite); + return LivechatVisitors.updateLivechatDataByToken(token, key, value, overwrite); } } diff --git a/packages/rocketchat-livechat/server/methods/transfer.js b/packages/rocketchat-livechat/server/methods/transfer.js index aa842498cd23..e2af06273488 100644 --- a/packages/rocketchat-livechat/server/methods/transfer.js +++ b/packages/rocketchat-livechat/server/methods/transfer.js @@ -1,4 +1,7 @@ /* eslint new-cap: [2, {"capIsNewExceptions": ["Match.Optional"]}] */ + +import LivechatVisitors from '../models/LivechatVisitors'; + Meteor.methods({ 'livechat:transfer'(transferData) { if (!Meteor.userId() || !RocketChat.authz.hasPermission(Meteor.userId(), 'view-l-room')) { @@ -13,7 +16,7 @@ Meteor.methods({ const room = RocketChat.models.Rooms.findOneById(transferData.roomId); - const guest = RocketChat.models.Users.findOneById(room.v._id); + const guest = LivechatVisitors.findOneById(room.v._id); const user = Meteor.user(); diff --git a/packages/rocketchat-livechat/server/models/LivechatInquiry.js b/packages/rocketchat-livechat/server/models/LivechatInquiry.js index 29439c3a0dbd..c7800535f61c 100644 --- a/packages/rocketchat-livechat/server/models/LivechatInquiry.js +++ b/packages/rocketchat-livechat/server/models/LivechatInquiry.js @@ -43,6 +43,21 @@ class LivechatInquiry extends RocketChat.models._Base { getStatus(inquiryId) { return this.findOne({'_id': inquiryId}).status; } + + updateVisitorStatus(token, status) { + const query = { + 'v.token': token, + status: 'open' + }; + + const update = { + $set: { + 'v.status': status + } + }; + + return this.update(query, update); + } } RocketChat.models.LivechatInquiry = new LivechatInquiry(); diff --git a/packages/rocketchat-livechat/server/models/LivechatVisitors.js b/packages/rocketchat-livechat/server/models/LivechatVisitors.js new file mode 100644 index 000000000000..d9eb7e7ce983 --- /dev/null +++ b/packages/rocketchat-livechat/server/models/LivechatVisitors.js @@ -0,0 +1,195 @@ +import _ from 'underscore'; +import s from 'underscore.string'; + +class LivechatVisitors extends RocketChat.models._Base { + constructor() { + super('livechat_visitor'); + } + + /** + * Gets visitor by token + * @param {string} token - Visitor token + */ + getVisitorByToken(token, options) { + const query = { + token + }; + + return this.findOne(query, options); + } + + /** + * Find visitors by _id + * @param {string} token - Visitor token + */ + findById(_id, options) { + const query = { + _id + }; + + return this.find(query, options); + } + + /** + * Gets visitor by token + * @param {string} token - Visitor token + */ + findVisitorByToken(token) { + const query = { + token + }; + + return this.find(query); + } + + updateLivechatDataByToken(token, key, value, overwrite = true) { + const query = { + token + }; + + if (!overwrite) { + const user = this.findOne(query, { fields: { livechatData: 1 } }); + if (user.livechatData && typeof user.livechatData[key] !== 'undefined') { + return true; + } + } + + const update = { + $set: { + [`livechatData.${ key }`]: value + } + }; + + return this.update(query, update); + } + + /** + * Find a visitor by their phone number + * @return {object} User from db + */ + findOneVisitorByPhone(phone) { + const query = { + 'phone.phoneNumber': phone + }; + + return this.findOne(query); + } + + /** + * Get the next visitor name + * @return {string} The next visitor name + */ + getNextVisitorUsername() { + const settingsRaw = RocketChat.models.Settings.model.rawCollection(); + const findAndModify = Meteor.wrapAsync(settingsRaw.findAndModify, settingsRaw); + + const query = { + _id: 'Livechat_guest_count' + }; + + const update = { + $inc: { + value: 1 + } + }; + + const livechatCount = findAndModify(query, null, update); + + return `guest-${ livechatCount.value.value + 1 }`; + } + + updateById(_id, update) { + return this.update({ _id }, update); + } + + saveGuestById(_id, data) { + const setData = {}; + const unsetData = {}; + + if (data.name) { + if (!_.isEmpty(s.trim(data.name))) { + setData.name = s.trim(data.name); + } else { + unsetData.name = 1; + } + } + + if (data.email) { + if (!_.isEmpty(s.trim(data.email))) { + setData.visitorEmails = [ + { address: s.trim(data.email) } + ]; + } else { + unsetData.visitorEmails = 1; + } + } + + if (data.phone) { + if (!_.isEmpty(s.trim(data.phone))) { + setData.phone = [ + { phoneNumber: s.trim(data.phone) } + ]; + } else { + unsetData.phone = 1; + } + } + + const update = {}; + + if (!_.isEmpty(setData)) { + update.$set = setData; + } + + if (!_.isEmpty(unsetData)) { + update.$unset = unsetData; + } + + if (_.isEmpty(update)) { + return true; + } + + return this.update({ _id }, update); + } + + findOneGuestByEmailAddress(emailAddress) { + const query = { + 'visitorEmails.address': new RegExp(`^${ s.escapeRegExp(emailAddress) }$`, 'i') + }; + + return this.findOne(query); + } + + saveGuestEmailPhoneById(_id, emails, phones) { + const update = { + $addToSet: {} + }; + + const saveEmail = [].concat(emails) + .filter(email => email && email.trim()) + .map(email => { + return { address: email }; + }); + + if (saveEmail.length > 0) { + update.$addToSet.visitorEmails = { $each: saveEmail }; + } + + const savePhone = [].concat(phones) + .filter(phone => phone && phone.trim().replace(/[^\d]/g, '')) + .map(phone => { + return { phoneNumber: phone }; + }); + + if (savePhone.length > 0) { + update.$addToSet.phone = { $each: savePhone }; + } + + if (!update.$addToSet.visitorEmails && !update.$addToSet.phone) { + return; + } + + return this.update({ _id }, update); + } +} + +export default new LivechatVisitors(); diff --git a/packages/rocketchat-livechat/server/models/Rooms.js b/packages/rocketchat-livechat/server/models/Rooms.js index 7fb8e5176a61..19003d79cb8e 100644 --- a/packages/rocketchat-livechat/server/models/Rooms.js +++ b/packages/rocketchat-livechat/server/models/Rooms.js @@ -117,11 +117,11 @@ RocketChat.models.Rooms.findByVisitorId = function(visitorId) { return this.find(query); }; -RocketChat.models.Rooms.findOneOpenByVisitorId = function(visitorId, roomId) { +RocketChat.models.Rooms.findOneOpenByVisitorToken = function(token, roomId) { const query = { _id: roomId, open: true, - 'v._id': visitorId + 'v.token': token }; return this.findOne(query); @@ -150,12 +150,11 @@ RocketChat.models.Rooms.closeByRoomId = function(roomId, closeInfo) { _id: roomId }, { $set: { - closedBy: { - _id: closeInfo.user._id, - username: closeInfo.user.username - }, + closer: closeInfo.closer, + closedBy: closeInfo.closedBy, closedAt: closeInfo.closedAt, - chatDuration: closeInfo.chatDuration + chatDuration: closeInfo.chatDuration, + 'v.status': 'offline' }, $unset: { open: 1 @@ -204,3 +203,18 @@ RocketChat.models.Rooms.saveCRMDataByRoomId = function(roomId, crmData) { return this.update(query, update); }; + +RocketChat.models.Rooms.updateVisitorStatus = function(token, status) { + const query = { + 'v.token': token, + open: true + }; + + const update = { + $set: { + 'v.status': status + } + }; + + return this.update(query, update); +}; diff --git a/packages/rocketchat-livechat/server/models/Users.js b/packages/rocketchat-livechat/server/models/Users.js index e44d648afd01..d34c072964e3 100644 --- a/packages/rocketchat-livechat/server/models/Users.js +++ b/packages/rocketchat-livechat/server/models/Users.js @@ -1,5 +1,3 @@ -import _ from 'underscore'; -import s from 'underscore.string'; /** * Sets an user as (non)operator * @param {string} _id - User's _id @@ -104,32 +102,6 @@ RocketChat.models.Users.getNextAgent = function() { } }; -/** - * Gets visitor by token - * @param {string} token - Visitor token - */ -RocketChat.models.Users.getVisitorByToken = function(token, options) { - const query = { - 'profile.guest': true, - 'profile.token': token - }; - - return this.findOne(query, options); -}; - -/** - * Gets visitor by token - * @param {string} token - Visitor token - */ -RocketChat.models.Users.findVisitorByToken = function(token) { - const query = { - 'profile.guest': true, - 'profile.token': token - }; - - return this.find(query); -}; - /** * Change user's livechat status * @param {string} token - Visitor token @@ -168,119 +140,6 @@ RocketChat.models.Users.openOffice = function() { }); }; -RocketChat.models.Users.updateLivechatDataByToken = function(token, key, value, overwrite = true) { - const query = { - 'profile.token': token - }; - - if (!overwrite) { - const user = this.findOne(query, { fields: { livechatData: 1 } }); - if (user.livechatData && typeof user.livechatData[key] !== 'undefined') { - return true; - } - } - - const update = { - $set: { - [`livechatData.${ key }`]: value - } - }; - - return this.update(query, update); -}; - -/** - * Find a visitor by their phone number - * @return {object} User from db - */ -RocketChat.models.Users.findOneVisitorByPhone = function(phone) { - const query = { - 'phone.phoneNumber': phone - }; - - return this.findOne(query); -}; - -/** - * Get the next visitor name - * @return {string} The next visitor name - */ -RocketChat.models.Users.getNextVisitorUsername = function() { - const settingsRaw = RocketChat.models.Settings.model.rawCollection(); - const findAndModify = Meteor.wrapAsync(settingsRaw.findAndModify, settingsRaw); - - const query = { - _id: 'Livechat_guest_count' - }; - - const update = { - $inc: { - value: 1 - } - }; - - const livechatCount = findAndModify(query, null, update); - - return `guest-${ livechatCount.value.value + 1 }`; -}; - -RocketChat.models.Users.saveGuestById = function(_id, data) { - const setData = {}; - const unsetData = {}; - - if (data.name) { - if (!_.isEmpty(s.trim(data.name))) { - setData.name = s.trim(data.name); - } else { - unsetData.name = 1; - } - } - - if (data.email) { - if (!_.isEmpty(s.trim(data.email))) { - setData.visitorEmails = [ - { address: s.trim(data.email) } - ]; - } else { - unsetData.visitorEmails = 1; - } - } - - if (data.phone) { - if (!_.isEmpty(s.trim(data.phone))) { - setData.phone = [ - { phoneNumber: s.trim(data.phone) } - ]; - } else { - unsetData.phone = 1; - } - } - - const update = {}; - - if (!_.isEmpty(setData)) { - update.$set = setData; - } - - if (!_.isEmpty(unsetData)) { - update.$unset = unsetData; - } - - if (_.isEmpty(update)) { - return true; - } - - return this.update({ _id }, update); -}; - -RocketChat.models.Users.findOneGuestByEmailAddress = function(emailAddress) { - const query = { - 'visitorEmails.address': new RegExp(`^${ s.escapeRegExp(emailAddress) }$`, 'i') - }; - - return this.findOne(query); -}; - RocketChat.models.Users.getAgentInfo = function(agentId) { const query = { _id: agentId diff --git a/packages/rocketchat-livechat/server/publications/visitorInfo.js b/packages/rocketchat-livechat/server/publications/visitorInfo.js index 1e1462b68d51..b09b0d9dac8f 100644 --- a/packages/rocketchat-livechat/server/publications/visitorInfo.js +++ b/packages/rocketchat-livechat/server/publications/visitorInfo.js @@ -1,3 +1,5 @@ +import LivechatVisitors from '../models/LivechatVisitors'; + Meteor.publish('livechat:visitorInfo', function({ rid: roomId }) { if (!this.userId) { return this.error(new Meteor.Error('error-not-authorized', 'Not authorized', { publish: 'livechat:visitorInfo' })); @@ -10,7 +12,7 @@ Meteor.publish('livechat:visitorInfo', function({ rid: roomId }) { const room = RocketChat.models.Rooms.findOneById(roomId); if (room && room.v && room.v._id) { - return RocketChat.models.Users.findById(room.v._id); + return LivechatVisitors.findById(room.v._id); } else { return this.ready(); } diff --git a/packages/rocketchat-livechat/server/sendMessageBySMS.js b/packages/rocketchat-livechat/server/sendMessageBySMS.js index fc849bc9be9c..313d3ce6370f 100644 --- a/packages/rocketchat-livechat/server/sendMessageBySMS.js +++ b/packages/rocketchat-livechat/server/sendMessageBySMS.js @@ -1,3 +1,5 @@ +import LivechatVisitors from './models/LivechatVisitors'; + RocketChat.callbacks.add('afterSaveMessage', function(message, room) { // skips this callback if the message was edited if (message.editedAt) { @@ -29,7 +31,7 @@ RocketChat.callbacks.add('afterSaveMessage', function(message, room) { return message; } - const visitor = RocketChat.models.Users.getVisitorByToken(room.v.token); + const visitor = LivechatVisitors.getVisitorByToken(room.v.token); if (!visitor || !visitor.profile || !visitor.phone || visitor.phone.length === 0) { return message; diff --git a/packages/rocketchat-livechat/server/startup.js b/packages/rocketchat-livechat/server/startup.js index 11c7792cff51..1af415b4fbdc 100644 --- a/packages/rocketchat-livechat/server/startup.js +++ b/packages/rocketchat-livechat/server/startup.js @@ -4,11 +4,11 @@ Meteor.startup(() => { }); RocketChat.authz.addRoomAccessValidator(function(room, user) { - return room.t === 'l' && RocketChat.authz.hasPermission(user._id, 'view-livechat-rooms'); + return room.t === 'l' && user && RocketChat.authz.hasPermission(user._id, 'view-livechat-rooms'); }); - RocketChat.authz.addRoomAccessValidator(function(room, user) { - return room.t === 'l' && room.v && room.v._id === user._id; + RocketChat.authz.addRoomAccessValidator(function(room, user, extraData) { + return room.t === 'l' && extraData && extraData.token && room.v && room.v.token === extraData.token; }); RocketChat.callbacks.add('beforeLeaveRoom', function(user, room) { diff --git a/packages/rocketchat-livechat/server/visitorStatus.js b/packages/rocketchat-livechat/server/visitorStatus.js new file mode 100644 index 000000000000..4c31c386e71c --- /dev/null +++ b/packages/rocketchat-livechat/server/visitorStatus.js @@ -0,0 +1,9 @@ +/* globals UserPresenceEvents */ +Meteor.startup(() => { + UserPresenceEvents.on('setStatus', (session, status, metadata) => { + if (metadata && metadata.visitor) { + RocketChat.models.LivechatInquiry.updateVisitorStatus(metadata.visitor, status); + RocketChat.models.Rooms.updateVisitorStatus(metadata.visitor, status); + } + }); +}); diff --git a/packages/rocketchat-tokenpass/server/startup.js b/packages/rocketchat-tokenpass/server/startup.js index 143e404d94ec..e6c0acfebde2 100644 --- a/packages/rocketchat-tokenpass/server/startup.js +++ b/packages/rocketchat-tokenpass/server/startup.js @@ -23,7 +23,7 @@ function validateTokenAccess(userData, roomData) { Meteor.startup(function() { RocketChat.authz.addRoomAccessValidator(function(room, user) { - if (!room.tokenpass) { + if (!room.tokenpass || !user) { return false; } diff --git a/private/node_scripts/auto-translate.js b/private/node_scripts/auto-translate.js index e78ad5eabc7f..1e7c2fc194d3 100644 --- a/private/node_scripts/auto-translate.js +++ b/private/node_scripts/auto-translate.js @@ -15,7 +15,7 @@ googleTranslate.getSupportedLanguages(function(err, langs) { return; } - async.eachSeries(['../../packages/rocketchat-lib/i18n/', '../../packages/rocketchat-livechat/app/i18n/'], function(path, callback) { + async.eachSeries(['../../packages/rocketchat-lib/i18n/', '../../packages/rocketchat-livechat/.app/i18n/'], function(path, callback) { console.log(`Translating files in: ${ path }`); const enContents = fs.readFileSync(`${ path }en.i18n.json`, 'utf-8'); const enUnsorted = JSON.parse(enContents); diff --git a/server/methods/canAccessRoom.js b/server/methods/canAccessRoom.js index dbb1d468b448..a21be60f438b 100644 --- a/server/methods/canAccessRoom.js +++ b/server/methods/canAccessRoom.js @@ -1,16 +1,10 @@ Meteor.methods({ - canAccessRoom(rid, userId) { + canAccessRoom(rid, userId, extraData) { check(rid, String); check(userId, Match.Maybe(String)); let user; - if (!userId && RocketChat.settings.get('Accounts_AllowAnonymousRead') === false) { - throw new Meteor.Error('error-invalid-user', 'Invalid user', { - method: 'canAccessRoom' - }); - } - if (userId) { user = RocketChat.models.Users.findOneById(userId, { fields: { @@ -33,13 +27,19 @@ Meteor.methods({ const room = RocketChat.models.Rooms.findOneById(rid); if (room) { - if (RocketChat.authz.canAccessRoom.call(this, room, user)) { + if (RocketChat.authz.canAccessRoom.call(this, room, user, extraData)) { if (user) { room.username = user.username; } return room; } + if (!userId && RocketChat.settings.get('Accounts_AllowAnonymousRead') === false) { + throw new Meteor.Error('error-invalid-user', 'Invalid user', { + method: 'canAccessRoom' + }); + } + return false; } else { throw new Meteor.Error('error-invalid-room', 'Invalid room', { diff --git a/server/methods/loadHistory.js b/server/methods/loadHistory.js index d0d7fb21e264..76ef737b2eef 100644 --- a/server/methods/loadHistory.js +++ b/server/methods/loadHistory.js @@ -1,5 +1,3 @@ -import _ from 'underscore'; - const hideMessagesOfType = []; RocketChat.settings.get(/Message_HideType_.+/, function(key, value) { @@ -43,70 +41,6 @@ Meteor.methods({ return false; } - const options = { - sort: { - ts: -1 - }, - limit - }; - - if (!RocketChat.settings.get('Message_ShowEditedStatus')) { - options.fields = { - editedAt: 0 - }; - } - - let records; - if (end != null) { - records = RocketChat.models.Messages.findVisibleByRoomIdBeforeTimestampNotContainingTypes(rid, end, hideMessagesOfType, options).fetch(); - } else { - records = RocketChat.models.Messages.findVisibleByRoomIdNotContainingTypes(rid, hideMessagesOfType, options).fetch(); - } - - const UI_Use_Real_Name = RocketChat.settings.get('UI_Use_Real_Name') === true; - - const messages = records.map((message) => { - message.starred = _.findWhere(message.starred, { - _id: fromId - }); - if (message.u && message.u._id && UI_Use_Real_Name) { - const user = RocketChat.models.Users.findOneById(message.u._id); - message.u.name = user && user.name; - } - if (message.mentions && message.mentions.length && UI_Use_Real_Name) { - message.mentions.forEach((mention) => { - const user = RocketChat.models.Users.findOneById(mention._id); - mention.name = user && user.name; - }); - } - return message; - }); - - let unreadNotLoaded = 0; - let firstUnread; - - if (ls != null) { - const firstMessage = messages[messages.length - 1]; - - if ((firstMessage != null ? firstMessage.ts : undefined) > ls) { - delete options.limit; - - const unreadMessages = RocketChat.models.Messages.findVisibleByRoomIdBetweenTimestampsNotContainingTypes(rid, ls, firstMessage.ts, hideMessagesOfType, { - limit: 1, - sort: { - ts: 1 - } - }); - - firstUnread = unreadMessages.fetch()[0]; - unreadNotLoaded = unreadMessages.count(); - } - } - - return { - messages, - firstUnread, - unreadNotLoaded - }; + return RocketChat.loadMessageHistory({ userId: fromId, rid, end, limit, ls }); } }); diff --git a/server/startup/migrations/v106.js b/server/startup/migrations/v106.js new file mode 100644 index 000000000000..7508a19901a3 --- /dev/null +++ b/server/startup/migrations/v106.js @@ -0,0 +1,46 @@ +import LivechatVisitors from 'meteor/rocketchat:livechat/server/models/LivechatVisitors'; + +RocketChat.Migrations.add({ + version: 106, + up() { + const visitors = Meteor.users.find({ type: 'visitor' }); + const total = visitors.count(); + let current = 1; + + console.log('Migrating livechat visitors, this may take a while ...'); + + Meteor.setTimeout(() => { + visitors.forEach(user => { + console.log(`Migrating visitor ${ current++ }/${ total }`); + + const { + _id, + name, + username, + deparment, + userAgent, + ip, + host, + visitorEmails, + phone + } = user; + LivechatVisitors.insert({ + _id, + name, + username, + deparment, + userAgent, + ip, + host, + visitorEmails, + phone, + token: user.profile.token + }); + + Meteor.users.remove({ _id }); + }); + + console.log('Livechat visitors migration finished.'); + }, 1000); + } +}); diff --git a/server/stream/messages.js b/server/stream/messages.js index 96afa00f6bc7..71c68bcdfe0d 100644 --- a/server/stream/messages.js +++ b/server/stream/messages.js @@ -3,9 +3,9 @@ this.msgStream = msgStream; msgStream.allowWrite('none'); -msgStream.allowRead(function(eventName) { +msgStream.allowRead(function(eventName, args) { try { - const room = Meteor.call('canAccessRoom', eventName, this.userId); + const room = Meteor.call('canAccessRoom', eventName, this.userId, args); if (!room) { return false;