Created
August 15, 2016 07:43
-
-
Save fathermerry/8111ae6f0418da884e5a4bbc2a97cd3a to your computer and use it in GitHub Desktop.
New Bot Code
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function Database() {} | |
Database.prototype.fetch = function() { | |
return localforage.getItem('eatdrinkbot:conversation'); | |
}; | |
Database.prototype.addMessage = function(message) { | |
var $this = this; | |
$this.fetch() | |
.then(function(db) { | |
var log = db.log || []; | |
if (log.length == 20) log.shift(); | |
log.push(message); | |
$this.update('log', log); | |
}); | |
}; | |
Database.prototype.update = function(key, value) { | |
this.fetch() | |
.then(function(db) { | |
var conversation = db; | |
conversation[key] = value; | |
localforage.setItem('eatdrinkbot:conversation', conversation); | |
}); | |
}; | |
function Conversation() { | |
this.db = new Database(); | |
this._init(); | |
} | |
Conversation.prototype._init = function() { | |
var $this = this; | |
$this.db.fetch() | |
.then(function(conversation) { | |
if (conversation && conversation.uid) { | |
return Promise.resolve(conversation); | |
} | |
$this.setupPubnub().then(function(conversation) { | |
conversation = conversation.json(); | |
localforage.setItem('eatdrinkbot:conversation', conversation); | |
return Promise.resolve(conversation); | |
}); | |
}) | |
.then(function(conversation) { | |
$this.uid = conversation.uid; | |
if (conversation.onboarded) { | |
var history = conversation.log || []; | |
return Promise.resolve(history); | |
} | |
$this.onboard('intro'); | |
return Promise.reject(); | |
}) | |
.then(function(history) { | |
UI.Thread.populate(history); | |
UI.InputField.addLiveEventListener(); | |
UI.initialize(); | |
return $this.start(); | |
}) | |
.catch(function(error) { | |
UI.InputField.disable({ message: error }); | |
}); | |
}; | |
Conversation.prototype.setupPubnub = function() { | |
return fetch('http://eatdrink-api.herokuapp.com/api/start', { | |
method: 'post', | |
body: JSON.stringify({ | |
message: 'okay' | |
}) | |
}); | |
}; | |
Conversation.prototype.onboard = function(stage, value) { | |
var $this = this; | |
switch (stage) { | |
case "intro": | |
var messages = ["How far! I'm here to help you find the ideal restaurant in Lagos.", "What do I call you?"]; | |
UI.initialize(); | |
UI.Thread.play(messages) | |
.then(function() { | |
UI.InputField.addDemoEventListener('name'); | |
}); | |
break; | |
case "guide": | |
var messages = []; | |
var name = value.capitalize(); | |
if (name.length > 15) { | |
name = UI.Utils.truncate(name, 15); | |
messages.push("That's a long one. I'll call you " + name); | |
messages.push("I assume this is your first time here."); | |
} else { | |
messages.push("Okay, " + name + ". I assume this is your first time here."); | |
} | |
messages.push("Frequently asked questions are on the left."); | |
messages.push("Your saved restaurants are listed on the right."); | |
messages.push("Type <b>okay</b> to start using the bot"); | |
UI.Thread.play(messages) | |
.then(function() { | |
UI.InputField.addDemoEventListener('okay'); | |
}); | |
break; | |
case "complete": | |
$this.db.update('onboarded', true); | |
$this.start() | |
.then(function() { | |
return $this.sendMessage('okay'); | |
}) | |
.then(function() { | |
UI.InputField.addLiveEventListener(); | |
}) | |
.catch(function(error) { | |
UI.InputField.disable({ message: error }); | |
}); | |
break; | |
default: | |
break; | |
} | |
}; | |
Conversation.prototype.start = function() { | |
var $this = this; | |
return new Promise(function(resolve, reject) { | |
UI.InputField.disable({ message: 'Connecting...' }); | |
if (typeof PUBNUB == 'undefined') { | |
reject('Could not connect to streaming server. Refresh page.'); | |
} | |
var pubnub = PUBNUB({ | |
subscribe_key: 'sub-c-af12bf36-bd26-11e5-8a35-0619f8945a4f', | |
publish_key: 'pub-c-ecfc3624-b442-47ec-a36d-3f4d35d86e83' | |
}); | |
pubnub.subscribe({ | |
channel: $this.uid, | |
message: function(message, env, channel) { | |
$this.respond(message); | |
}, | |
connect: function() { | |
UI.InputField.reset(); | |
resolve(); | |
console.log("Connected"); | |
}, | |
disconnect: function() { | |
UI.InputField.disable(); | |
console.log("Disconnected"); | |
}, | |
reconnect: function() { | |
UI.InputField.enable(); | |
console.log("Reconnected"); | |
}, | |
error: function() { | |
console.log("Network Error"); | |
}, | |
}); | |
}); | |
}; | |
Conversation.prototype.sendMessage = function(message) { | |
var $this = this; | |
UI.InputField.disable(); | |
return fetch('http://eatdrink-api.herokuapp.com/api/message', { | |
method: 'post', | |
headers: new Headers({ | |
'Content-Type': 'application/json' | |
}), | |
body: JSON.stringify({ | |
message: message, | |
uid: $this.uid | |
}) | |
}); | |
}; | |
Conversation.prototype.respond = function(message) { | |
this.db.addMessage(message); | |
UI.Thread.add(message); | |
}; | |
var Bot = new Conversation(); | |
// UI Stuff | |
var UI = {}; | |
UI.Elements = { | |
body: document.body, | |
thread: document.getElementById('thread'), | |
chatInput: document.getElementById('chat-input'), | |
tagInput: document.getElementById('tag-input'), | |
chatForm: document.getElementById('chat-form'), | |
chatField: document.getElementById('chat-field'), | |
chatWrapper: document.getElementById('chat-wrapper'), | |
overlay: document.getElementById('overlay'), | |
showTriggers: document.getElementsByClassName('show'), | |
hideTriggers: document.getElementsByClassName('hide') | |
}; | |
UI.initialize = function() { | |
String.prototype.capitalize = function() { | |
return this.charAt(0).toUpperCase() + this.slice(1); | |
}; | |
UI.Elements.body.style.display = ''; | |
UI.addEventListeners(); | |
}; | |
UI.addEventListeners = function() { | |
UI.Elements.chatInput.addEventListener('focus', function() { | |
setTimeout(function() { | |
UI.Elements.chatWrapper.style.height = window.innerHeight + 'px'; | |
document.body.scrollTop = document.documentElement.scrollTop = 0; | |
}, 300); | |
}); | |
UI.Elements.chatInput.addEventListener('blur', function() { | |
UI.Elements.chatWrapper.style.height = '100%'; | |
}); | |
for (var i = 0; i < UI.Elements.showTriggers.length; i++) { | |
element = UI.Elements.showTriggers[i]; | |
element.addEventListener('click', function() { | |
var divName = this.getAttribute('data-div'); | |
var divToShow = document.getElementById(divName); | |
var animation = divToShow.getAttribute('data-animate'); | |
UI.Elements.overlay.style.display = 'block'; | |
if (animation) { | |
var animation_classes = animation.split(" "); | |
for (var j = 0; j < animation_classes.length; j++) { | |
UI.Utils.addClass(divToShow, animation_classes[j]); | |
} | |
} | |
divToShow.style.zIndex = '100'; | |
divToShow.style.display = 'block'; | |
}); | |
} | |
for (var j = 0; j < UI.Elements.hideTriggers.length; j++) { | |
element = UI.Elements.showTriggers[j]; | |
element.addEventListener('click', function() { | |
var divName = this.getAttribute('data-div'); | |
var divToHide = document.getElementById(divName); | |
var animation = divToHide.getAttribute('data-animate'); | |
UI.Elements.overlay.style.display = 'none'; | |
if (animation) { | |
var animation_classes = animation.split(" "); | |
for (var j = 0; j < animation_classes.length; j++) { | |
UI.Utils.removeClass(divToHide, animation_classes[j]); | |
} | |
} | |
divToHide.style.zIndex = ''; | |
divToHide.style.display = 'none'; | |
}); | |
} | |
}; | |
UI.Utils = { | |
addClass: function(el, className) { | |
if (el.classList) | |
el.classList.add(className); | |
else | |
el.className += ' ' + className; | |
}, | |
removeClass: function(el, className) { | |
if (el.classList) | |
el.classList.remove(className); | |
else | |
el.className = el.className.replace(new RegExp('(^|\\b)' + className.split(' ').join('|') + '(\\b|$)', 'gi'), ' '); | |
}, | |
_checkIfRestart: function(message) { | |
if (message == 'rs' || message == 'restart') { | |
localforage.removeItem('eatdrinkbot:conversation'); | |
location.reload(); | |
return; | |
} | |
}, | |
truncate: function(string) { | |
string = string.split(" ")[0]; | |
if (string.length > length) { | |
string = string.substring(0, length); | |
} | |
return string; | |
} | |
}; | |
UI.Thread = { | |
populate: function(log) { | |
UI.Elements.thread.style.opacity = '0'; | |
log.forEach(function(message, index) { | |
if (message.options) delete message.options; | |
UI.Thread.add(message); | |
if (index + 1 == log.length) { | |
UI.Elements.thread.style.opacity = ''; | |
} | |
}); | |
}, | |
add: function(message) { | |
var div; | |
if (message.type == 'location') { | |
div = UI.Thread._createLocation(message); | |
} else { | |
div = UI.Thread._createBubble(message); | |
} | |
var clear = document.createElement("div"); | |
clear.className = "clear"; | |
UI.Elements.thread.appendChild(div); | |
UI.Elements.thread.appendChild(clear); | |
UI.Elements.thread.scrollTop = UI.Elements.thread.offsetHeight; | |
if (message.options) UI.InputField.showTagInput(message); | |
}, | |
_createBubble: function(message) { | |
var div = document.createElement("div"); | |
div.className = message.is_app ? 'from-bot' : 'from-user'; | |
div.className += ' animated fadeInUp'; | |
if (message.spinner) div.className += ' sending-message'; | |
var content = document.createElement("p"); | |
content.innerHTML = message.body; | |
div.appendChild(content); | |
return div; | |
}, | |
_createLocation: function() { | |
// different UI for adding location | |
}, | |
removeSpinner: function() { | |
// find last message and remove spinner | |
}, | |
play: function(messages) { | |
return new Promise(function(resolve, reject) { | |
var delay = 500; | |
UI.InputField.disable(); | |
messages.forEach(function(message, index) { | |
var nextMessage = messages[index + 1]; | |
setTimeout(function() { | |
UI.Thread.add({ is_app: true, body: message }); | |
if (!nextMessage) { | |
UI.InputField.enable(); | |
resolve(); | |
} | |
}, delay); | |
if (nextMessage) { | |
var extraDelay = (nextMessage.length / 15) * 500; | |
delay += extraDelay; | |
} | |
}); | |
}); | |
} | |
}; | |
UI.InputField = { | |
showTagInput: function(message) { | |
UI.Elements.chatInput.style.display = 'none'; | |
UI.Elements.tagInput.style.display = 'block'; | |
UI.Utils.addClass(UI.Elements.chatField, 'has-tag-input'); | |
UI.InputField._populateTagInput(message.options); | |
}, | |
hideTagInput: function() { | |
UI.Elements.tagInput.style.display = 'none'; | |
UI.Elements.tagInput.innerHTML = ''; | |
UI.Utils.removeClass(UI.Elements.chatField, 'has-tag-input'); | |
UI.Elements.chatInput.style.display = 'block'; | |
UI.Elements.chatInput.focus(); | |
UI.InputField.reset(); | |
}, | |
_populateTagInput: function(options) { | |
for (var i = 0; i < options.length; i++) { | |
var tag = document.createElement("span"); | |
tag.setAttribute('data-index', i); | |
tag.innerHTML = options[i].name; | |
tag = UI.InputField._addTagEventListener(tag, options[i]); | |
UI.Elements.tagInput.appendChild(tag); | |
} | |
}, | |
_addTagEventListener: function(tag, option) { | |
tag.addEventListener('click', function() { | |
UI.Thread.add({ body: option.name }); | |
UI.InputField.disable(); | |
var index = this.getAttribute('data-index'); | |
Bot.sendMessage(index).then(function() { | |
UI.InputField.hideTagInput(); | |
}); | |
}); | |
return tag; | |
}, | |
addLiveEventListener: function() { | |
UI.Elements.chatForm.addEventListener('submit', function(e) { | |
e.preventDefault(); | |
var message = UI.Elements.chatInput.value; | |
if (!message) return; | |
UI.Utils._checkIfRestart(message); | |
UI.Thread.add({ body: message, spinner: true }); | |
Bot.sendMessage(message).then(function() { | |
UI.Thread.removeSpinner(); | |
UI.InputField.reset(); | |
Bot.db.addMessage({ body: message }); | |
}); | |
}); | |
}, | |
addDemoEventListener: function(listener) { | |
switch (listener) { | |
case "name": | |
UI.Elements.chatForm.addEventListener('submit', UI.InputField._listenForName); | |
break; | |
case "okay": | |
UI.Elements.chatForm.removeEventListener('submit', UI.InputField._listenForName); | |
UI.Elements.chatForm.addEventListener('submit', UI.InputField._listenForOkay); | |
break; | |
} | |
}, | |
_listenForName: function(e) { | |
e.preventDefault(); | |
var name = UI.InputField._validateMessage(); | |
if (name) Bot.onboard('guide', name); | |
}, | |
_listenForOkay: function(e) { | |
e.preventDefault(); | |
var message = UI.InputField._validateMessage(); | |
if (message) { | |
message = message.toLowerCase(); | |
if (message == 'ok' || message == 'okay') { | |
UI.Elements.chatForm.removeEventListener('submit', UI.InputField._listenForOkay); | |
Bot.onboard('complete'); | |
} | |
} | |
}, | |
_validateMessage: function() { | |
var message = UI.Elements.chatInput.value; | |
if (!message) return false; | |
UI.Thread.add({ body: message }); | |
UI.InputField.reset(); | |
return message; | |
}, | |
disable: function(options) { | |
options = options || {}; | |
if (options.message) UI.Elements.chatInput.value = options.message; | |
UI.Elements.chatInput.setAttribute('disabled', true); | |
UI.Utils.addClass(UI.Elements.tagInput, 'disabled'); | |
}, | |
enable: function() { | |
UI.Elements.chatInput.removeAttribute('disabled'); | |
UI.Utils.removeClass(UI.Elements.tagInput, 'disabled'); | |
UI.Elements.chatInput.focus(); | |
}, | |
reset: function() { | |
UI.Elements.chatInput.value = ''; | |
UI.InputField.enable(); | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment