lprest
Version:
Node.js packages to access LivePerson Rest APIs
400 lines (342 loc) • 11.9 kB
JavaScript
var _ = require("lodash");
var util = new require("util");
var Q = require("q");
var events = require("events");
/*
CHAT
{ link:
{ [Function]
all:
[ { rel: 'self',
href: 'https://dev.liveperson.net/api/account/P9700259/chat/H5743203613919339957-5243781235070630709K8394878' },
{ rel: 'events',
href: 'https://dev.liveperson.net/api/account/P9700259/chat/H5743203613919339957-5243781235070630709K8394878/events' },
{ rel: 'info',
href: 'https://dev.liveperson.net/api/account/P9700259/chat/H5743203613919339957-5243781235070630709K8394878/info' },
{ rel: 'next',
href: 'https://dev.liveperson.net/api/account/P9700259/chat/H5743203613919339957-5243781235070630709K8394878?from=2' },
{ rel: 'transcript-request',
href: 'https://dev.liveperson.net/api/account/P9700259/chat/H5743203613919339957-5243781235070630709K8394878/transcriptRequest' },
{ rel: 'exit-survey',
href: 'https://dev.liveperson.net/api/account/P9700259/chat/H5743203613919339957-5243781235070630709K8394878/exitSurvey' },
{ rel: 'custom-variables',
href: 'https://dev.liveperson.net/api/account/P9700259/chat/H5743203613919339957-5243781235070630709K8394878/customVariables' },
{ rel: 'visit-session',
href: 'https://dev.liveperson.net/api/account/P9700259/visit/H5743203613919339957K8394878' } ] },
events:
{ link:
[ { '@href': 'https://dev.liveperson.net/api/account/P9700259/chat/H5743203613919339957-5243781235070630709K8394878/events',
'@rel': 'self' },
{ '@href': 'https://dev.liveperson.net/api/account/P9700259/chat/H5743203613919339957-5243781235070630709K8394878/events?from=2',
'@rel': 'next' } ],
event:
[ { '@id': '0',
'@type': 'state',
time: '2014-12-02T08:22:42.575-05:00',
state: 'waiting' },
{ '@id': '1',
'@type': 'line',
time: '2014-12-02T08:22:42.580-05:00',
textType: 'plain',
text: 'Please hold for a moment. I will be right with you.',
by: 'info',
source: 'system',
systemMessageId: 4,
subType: 'REGULAR' } ] },
info:
{ link:
[ { '@href': 'https://dev.liveperson.net/api/account/P9700259/chat/H5743203613919339957-5243781235070630709K8394878/info',
'@rel': 'self' },
{ '@href': 'https://dev.liveperson.net/api/account/P9700259/chat/H5743203613919339957-5243781235070630709K8394878/info/visitorName',
'@rel': 'visitor-name' },
{ '@href': 'https://dev.liveperson.net/api/account/P9700259/chat/H5743203613919339957-5243781235070630709K8394878/info/visitorTyping',
'@rel': 'visitor-typing' },
{ '@href': 'https://dev.liveperson.net/api/account/P9700259/chat/H5743203613919339957-5243781235070630709K8394878/info/agentTyping',
'@rel': 'agent-typing' } ],
state: 'waiting',
chatSessionKey: 'H5743203613919339957-5243781235070630709K8394878',
skillName: 'LiveEngage',
agentName: 'Adrian',
agentId: 3,
startTime: '2014-12-02T08:22:42.580-05:00',
duration: 0,
lastUpdate: '2014-12-02T08:22:43.498-05:00',
chatTimeout: 40,
visitorId: 15115019951810,
agentTyping: 'not-typing',
visitorTyping: 'not-typing',
visitorName: 'test',
rtSessionId: 4294973566 } }
*/
var STATE_ENDED = "ended";
var Chat = function Chat(accessor) {
events.EventEmitter.call(this);
var chat = this;
this._accessor = accessor;
this._entity = null;
function eventsLoaded(events) {
chat._entity.events = events;
var array = events.event;
if (typeof array === "undefined") {
array = [];
}
else if (!_.isArray(array)) {
array = [array];
}
array.forEach(function (event) {
process.nextTick(function () {
chat.emit(event["@type"], event);
});
});
return array;
};
function infoLoaded(info) {
chat._entity.info = info;
process.nextTick(function () {
chat.emit('info', info);
});
return info;
};
/**
* The transition-state event is used internally, it is used when a new state event arrives
* or when we manually use chat.end();
*/
this.on("transition-state", function updateInternalState(state) {
chat._entity.info.state = state;
});
this.init = function () {
return accessor.get().then(function (chatEntity) {
chat._entity = chatEntity;
infoLoaded(accessor.asEntity(chatEntity.info));
eventsLoaded(accessor.asEntity(chatEntity.events));
}).thenResolve(chat);
};
/**
* Loads this chat's info and is resolved with the info entity.
* @returns {*}
*/
this.info = function () {
return chat._entity.info.link("self").get().then(infoLoaded);
};
/**
* Loads all events and dispatches them on this chat object, resolved with the newly received event array
* @returns {*}
*/
this.events = function () {
return chat._entity.events.link("next").get().then(function (events) {
return eventsLoaded(events);
});
};
/**
* Set the transcript email address.
* Returns a promise that is resolved if success, or fails otherwise
* @param email string email
*/
this.transcriptRequest = function (email) {
return chat._entity.link("transcript-request").set({email: email}).thenResolve(); //Resolving with undefined.
};
/**
* Sends a new event to the chat.
* Returns a promise that is resolved if success, or fails otherwise
*/
this.sendEvent = function (event) {
return chat._entity.link("events").create({
event: event
}).thenResolve(); //Resolving with undefined.
};
/**
* Sends a new event to the chat.
* Returns a promise that is resolved if success, or fails otherwise
*/
this.end = function () {
if(chat.state() !== STATE_ENDED) {
return chat.sendState(STATE_ENDED).then(function() {
chat.emit("transition-state", STATE_ENDED);
});
}
return Q();
};
/**
* Sends a new state to the chat
* Returns a promise that is resolved if success, or fails otherwise
*/
this.sendState = function (state) {
var event = {
"@type": "state",
state: state
};
return chat.sendEvent(event);
};
/**
* Sends a new state to the chat
* Returns a promise that is resolved if success, or fails otherwise
*/
this.whenState = function (state) {
if(chat.state() === state) {
return Q();
}
var defer = Q.defer();
var listener = function(data) {
if(data.state === state) {
chat.removeListener("state",listener);
defer.resolve();
}
};
chat.on("state", listener);
return defer.promise;
};
/**
* Sends a new line of text to the chat
* Returns a promise that is resolved if success, or fails otherwise
*/
this.sendLine = function (text) {
var event = {
"@type": "line",
text: text
};
return chat.sendEvent(event);
};
/*
* Get the transcript email address.
* Returns a promise that is resolved if success, or fails otherwise
* @param name string survey name
*/
this.exitSurvey = function (name) {
return chat._entity.link("transcript-request")
.get(name ? {"surveyName": name} : {}).thenResolve(); //Resolving with undefined.
};
/*
* Gets the visitor session entity, not yet implemented further
*/
this.visitSession = function (name) {
return chat._entity.link("visit-session").get();
};
/*
* Gets the chat session key
*/
this.chatSessionKey = function () {
return chat._entity.info.chatSessionKey;
};
/*
* Gets the agentName associated with chat session
*/
this.agentName = function () {
return chat._entity.info.agentName;
};
/*
* Gets the agent Id associated with chat session
*/
this.agentId = function () {
return chat._entity.info.agentId;
};
/**
* Gets the latest info we received.
* @returns {*}
*/
this.state = function () {
return chat._entity && chat._entity.info.state;
};
/**
* Gets this chat's location
* @returns {*}
*/
this.location = function () {
return chat._entity.link.map.self;
};
/*
* Gets the visitor ID
*/
this.visitorId = function () {
return chat._entity.info.visitorId;
};
//Updating latest info
chat.on("state", function(data) {
this.emit("tranision-state", data.state);
});
/*
* Get the transcript email address.
* Returns a promise that is resolved if success, or fails otherwise
* @param email string email
*/
this.customVariables = function (map) {
var array = [];
for (var key in map) {
if (map.hasOwnProperty(key)) {
array.push({name: key, value: map[key]});
}
}
return chat._entity.link("custom-variables")
.set({
"customVariables": {
"customVariable": array
}
}).thenResolve(); //Resolving with undefined.
};
/*
* Starts chat polling at the specified interval. If an error occurs polling stops.
* Returns a promise with an additional method 'stop' that stops polling (if a request was already sent it will
* complete).
* The promise is rejected if an error has occurred in a poll.
* The promise is resolved after the poller was stopped.
* @param halting if true the poller will stop itself when chat is ended.
*/
this.polling = function (timing, maxIdleTime, halting) {
var poller = Q.defer();
var timer = 0;
var lastEvents = Date.now();
function schedulePoll() {
if (timing > 0) {
timer = setTimeout(function () {
chat.events().then(function (events) {
poller.notify(events);
var now = Date.now();
if (events.length) {
lastEvents = now;
}
if (maxIdleTime && now - lastEvents > maxIdleTime) {
stop();
}
else {
schedulePoll();
}
}, poller.reject); //Passing the events to the poller promise.
}, timing);
}
}
var stop = poller.promise.stop = function stop(emitStopEvent) {
console.error("POLLING STOP!");
poller.resolve();
timing = 0; //stop.
if (timer !== 0) {
clearTimeout(timer);
timer = 0;
}
};
if(halting) {
//This will never be rejected unless a programmatic error.
chat.whenState(STATE_ENDED).done(stop);
}
schedulePoll();
return poller.promise;
};
/**
* When ended is a special promise that will be called whether we received an "ended" state event or if we successfully
* sent an "ended" event to Liveperson.
* That is, this promise is resolved whenever an end event has been sent or received.
*/
this.whenEnded = Q.Promise(function(resolve) {
chat.whenState(STATE_ENDED, resolve);
chat.on("transition-state", function(state) {
if(state === STATE_ENDED) {
resolve();
}
});
});
this.whenState(STATE_ENDED, function() {
this.removeAllListeners();
});
};
util.inherits(Chat, events.EventEmitter);
Chat.fromPath = function(token, path) {
}
module.exports = Chat;