Kara is a digital assistent aimed to help in any daily situation. => Moved to /LunaDev/luna-development https://luna-development.net/kara/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

614 lines
20 KiB

let kara = new Vue({
el: '#kara',
data: {
features: {
changeName: false,
themes: true,
ownReactions: true,
setNameAtStart: true
},
messages: [],
lastMessage: null,
lastMessageData: {},
name: 'Kara',
location: null,
chatbox: null,
isTyping: false,
askedForName: false,
takeTodo: false,
username: '',
themes: null,
activeTheme: 'slate',
talk: false,
addModal: {
includeAll: false,
keywords: '',
responses: [
'Answer #1'
]
},
settingsModal: {
name: null,
username: null,
location: ''
},
todos: [],
answers: [
{
includeAll: false,
keywords: [
'hi', 'hallo', 'servas', 'servus', 'hello'
],
responses: [
'Hey! :)',
'Hello!',
'How are you?'
]
},
{
includeAll: true,
keywords: [
'how', 'are', 'you'
],
responses: [
'I\'m good! Thanks for asking! :) How about you?',
'A bit tired from trying to do that think called "thinking".. i\'ll figure it out one day.',
'Wooo! I\'m hyped for party! You\'re in?'
]
}
]
},
mounted() {
this.getSavedData();
if (this.features.themes) {
this.getBootswatchThemes();
}
this.settingsModal = {
name: this.name,
username: this.username,
location: this.location
};
if (!this.username || this.username === "null") {
this.initialGreeting();
if (this.features.setNameAtStart) {
this.askForName();
}
this.isTyping = false;
} else {
this.welcomeBack();
}
document.getElementById('chatinput').focus();
document.title = this.name;
this.scrollDown();
},
methods: {
karaTalks(message) {
var speech = new SpeechSynthesisUtterance();
// Set the text and voice attributes.
speech.text = message;
speech.volume = 1;
speech.rate = 1;
speech.pitch = 1;
window.speechSynthesis.speak(speech);
},
addMessage(body, bot, me = false) {
this.messages.push({
body: body,
bot: bot,
command: body.search('/') === 0,
me: me,
time: Date.now()
})
},
botMessage(body) {
this.addMessage(body, true);
if (this.talk) {
this.karaTalks(body);
}
},
userMessage(body) {
this.addMessage(body, false);
this.lastMessage = body;
this.updateStorage();
},
initialGreeting() {
this.botMessage(
this.oneOf(
[
"Hi! I'm " + this.name + ". :)",
"Hi, nice to meet you! My name is " + this.name + ". :)"
]
)
);
this.updateStorage();
},
welcomeBack() {
this.botMessage(
this.oneOf(
[
"Hi! Haven't heard of you in a while. :)",
"Welcome back! :)",
"Hey :) Good to see you :)",
]
)
);
},
sendMessage() {
if (this.chatbox.trim() === '') {
return false;
}
if (this.chatbox.search('/me') !== 0) {
this.userMessage(this.chatbox);
}
this.scrollDown();
this.react(this.chatbox);
this.chatbox = '';
},
react(message, recursive = false) {
this.isTyping = true;
if (message.search('/') === 0 && !recursive) {
setTimeout(() => {
this.processCommands(message);
this.isTyping = false;
this.scrollDown();
}, 1000);
} else {
setTimeout(() => {
// Check commands
if (this.askedForName === true) {
this.setName(message);
} else if (this.takeTodo === true) {
this.saveTodo(message);
} else {
let answer = this.getAnswer(message);
if (answer) {
this.botMessage(answer);
}
this.updateStorage();
}
this.isTyping = false;
this.scrollDown();
}, 1800);
}
},
processCommands(message) {
if (this.checkForCommands(message, 'todo')) {
let todoToSave = this.checkForCommands(message, 'todo');
this.saveTodo(todoToSave);
}
if (this.checkForCommands(message, 'clear')) {
this.clearChat();
}
if (this.checkForCommands(message, 'weather')) {
this.checkWeather()
}
if (this.checkForCommands(message, 'joke')) {
this.tellJoke(this.checkForCommands(message, 'joke'))
}
if (this.checkForCommands(message, 'me')) {
this.meMessage(this.checkForCommands(message, 'me'))
}
if (this.checkForCommands(message, 'say')) {
this.botMessage(this.checkForCommands(message, 'say'))
}
if (this.checkForCommands(message, 'talk')) {
this.talk = !this.talk;
alertify.notify('Speech ' + this.talk ? 'enabled' : 'disabled');
}
},
checkForCommands(message, commands) {
if (!Array.isArray(commands)) {
commands = [commands];
}
let commandFound = false;
let parameter = false;
commands.forEach((command) => {
if (commandFound) {
return;
}
let commandString = '/' + command;
if (message.search(commandString) === 0) {
parameter = message.replace(commandString, '').trim();
commandFound = true;
}
});
return parameter ? parameter : commandFound;
},
scrollDown() {
$('#chat-box').stop().animate({
scrollTop: ($('#chat-box')[0].scrollHeight * 10)
}, 800);
},
oneOf(answers) {
let amountOfAnswers = answers.length;
let randomIndex = Math.floor(Math.random() * (amountOfAnswers));
return this.convertWildcards(answers[randomIndex]);
},
convertWildcards(message) {
message = message.replace('$name$', this.username);
message = message.replace('$botname$', this.name);
return message;
},
includesOneOf(phrases, wordsToSearch) {
let includes = false;
wordsToSearch.forEach((searchWord) => {
if (phrases.includes(searchWord)) {
includes = true;
}
})
return includes;
},
includesAllOf(phrases, wordsToSearch) {
let includesAllPhrases = true;
wordsToSearch.forEach((searchWord) => {
if (!phrases.includes(searchWord)) {
includesAllPhrases = false;
}
})
return includesAllPhrases;
},
cleanupMessage(message) {
message = message.toLowerCase();
return message.replace('?', '')
.replace('!', '')
.replace('.', '')
.replace(',', '')
.replace('-', '')
.replace('_', '')
.replace('#', '')
.replace('\'', '')
.replace('"', '')
.replace('+', '')
.replace('*', '')
.replace('§', '')
.replace('$', '')
.replace('%', '')
.replace('&', '')
.replace('/', '')
.replace('(', '')
.replace(')', '')
.replace('=', '')
.replace('\\', '')
.replace('@', '')
.replace('~', '')
.replace('…', '');
},
getAnswer(message) {
message = this.cleanupMessage(message);
let phrases = message.split(' ');
let answer = undefined;
if (this.lastMessageData.joke && this.includesOneOf(phrases, ['another', 'more'])) {
this.tellJoke(this.lastMessageData.category);
}
if (this.includesAllOf(phrases, ['change', 'my', 'name'])) {
this.askedForName = true;
return "Please tell me how i should call you.";
}
if (this.includesAllOf(phrases, ['new', 'todo']) ||
this.includesAllOf(phrases, ['new', 'task']) ||
this.includesAllOf(phrases, ['take', 'todo']) ||
this.includesAllOf(phrases, ['save', 'to', 'clipboard'])
) {
this.takeTodo = true;
return this.oneOf([
"What do you wan't me to save for you?",
"Tell me what you wan't to save.",
"What is it you wan't to save?",
]);
}
if (this.includesAllOf(phrases, ['clear', 'chat'])) {
this.clearChat();
return false;
}
if (this.includesAllOf(phrases, ['weather']) &&
this.includesOneOf(phrases, ['how', 'whats'])
) {
this.checkWeather();
return false;
}
if (this.includesAllOf(phrases, ['knock', 'joke'])) {
this.tellJoke('knock-knock');
return false;
}
if (this.includesAllOf(phrases, ['joke']) && this.includesOneOf(phrases, ['coding', 'programming', 'code', 'it'])) {
this.tellJoke('programming');
return false;
}
if (this.includesAllOf(phrases, ['tell', 'joke']) ||
this.includesAllOf(phrases, ['something', 'funny']) ||
this.includesAllOf(phrases, ['cheer', 'me', 'up'])
) {
this.tellJoke('general');
return false;
}
if (this.includesAllOf(phrases, ['whats', 'the', 'time']) || this.includesAllOf(phrases, ['how', 'late'])) {
return "It's " + moment().format('LT');
}
if (this.includesAllOf(phrases, ['what', 'day', 'it'])) {
return "It's " + moment().format('dddd') + ".";
}
if (this.includesAllOf(phrases, ['what', 'date', 'it']) || this.includesAllOf(phrases, ['whats', 'the', 'date'])) {
return "It's " + moment().format('dddd') + ", " + moment().format('MMMM Do YYYY') + ".";
}
this.answers.forEach((answerOption) => {
if (answerOption.includeAll === true) {
if (this.includesAllOf(phrases, answerOption.keywords)) {
answer = this.oneOf(answerOption.responses);
}
} else {
if (this.includesOneOf(phrases, answerOption.keywords)) {
answer = this.oneOf(answerOption.responses);
}
}
});
if (answer) {
return answer;
}
return 'I don\'t know what to say..';
},
addResponseToInput() {
this.addModal.responses.push('');
},
addNewResponseContainer() {
let includeAll = this.addModal.includeAll;
let keywords = this.cleanupMessage(this.addModal.keywords).split(',');
let responses = this.addModal.responses;
this.answers.push({
includeAll: includeAll,
keywords: keywords,
responses: responses
});
this.addModal.includeAll = false;
this.addModal.keywords = '';
this.addModal.responses = [''];
this.updateStorage();
},
updateStorage() {
localStorage.setItem('name', this.name);
localStorage.setItem('username', this.username);
localStorage.setItem('activeTheme', this.activeTheme);
localStorage.setItem('messages', JSON.stringify(this.messages));
localStorage.setItem('answers', JSON.stringify(this.answers));
localStorage.setItem('todos', JSON.stringify(this.todos));
localStorage.setItem('lastMessage', JSON.stringify(this.lastMessage));
localStorage.setItem('location', JSON.stringify(this.location));
},
getSavedData() {
let savedName = localStorage.getItem('name');
this.name = savedName ? savedName : this.name;
let savedUsername = localStorage.getItem('username');
this.username = savedUsername ? savedUsername : null;
let savedActiveTheme = localStorage.getItem('activeTheme');
this.activeTheme = savedActiveTheme ? savedActiveTheme : 'slate';
let savedMessages = JSON.parse(localStorage.getItem('messages'));
this.messages = savedMessages ? savedMessages : [];
let savedAnswers = JSON.parse(localStorage.getItem('answers'));
this.answers = savedAnswers ? savedAnswers : [];
let savedTodos = JSON.parse(localStorage.getItem('todos'));
this.todos = savedTodos ? savedTodos : [];
let savedLastMessage = JSON.parse(localStorage.getItem('lastMessage'));
this.lastMessage = savedLastMessage ? savedLastMessage : null;
let savedLocation = JSON.parse(localStorage.getItem('location'));
this.location = savedLocation ? savedLocation : null;
this.scrollDown();
},
askForName() {
this.botMessage(
this.oneOf([
'May i ask for your name?',
'Whats your name? :)',
'How can i call you?',
'How did your developers call you? :)'
])
);
this.askedForName = true;
},
setName(message) {
this.username = message.trim();
this.settingsModal.username = this.username;
this.askedForName = false;
this.updateStorage();
this.botMessage(
this.oneOf([
"That's a beautiful name!",
"Okay, i'll call you " + this.username + " from now on :)",
"Nice to meet you, " + this.username + ". :D"
])
)
},
saveTodo(message) {
this.takeTodo = false;
this.todos.push({
time: moment(),
body: message,
checked: false
});
this.updateStorage();
this.botMessage(
this.oneOf([
"Saved! :)",
"You can read and check your todos in the clipboard-section. :)"
])
)
},
clearTodos() {
this.todos = [];
this.botMessage(
this.oneOf([
"Todos cleared. 🚮"
])
);
this.updateStorage();
},
saveSettings() {
this.name = this.settingsModal.name;
this.username = this.settingsModal.username;
this.location = this.settingsModal.location;
this.updateStorage();
// this.botMessage('Settings saved! :)');
this.scrollDown();
},
clearChat() {
this.messages = [];
this.botMessage(this.oneOf([
'Chat cleared.',
'Evidence destroyed.',
'Mind cleared.',
'Uhm.. i think forgot everything we ever talked about.. oops.',
'A fresh start!',
"You've got something to hide? Nevermind, gotcha fam. Chat is cleared now. 🐱👤"
]));
this.updateStorage();
},
checkWeather() {
let vue = this;
if (!vue.location) {
vue.botMessage('Please set your location in the settings. ⚙');
return;
}
let city = this.location.toLowerCase();
let url = 'http://api.openweathermap.org/data/2.5/weather?q=' + city + '&appid=8a1aa336da8899c1038bf6bd808d8961&units=metric';
axios.get(url)
.then(function (response) {
vue.botMessage('In ' + response.data.name + ' it\'s ' + response.data.main.temp.toFixed() + '°C with ' + response.data.weather[0].description + '.');
})
.catch(function (error) {
alert(error);
vue.botMessage('I couldn\'t check the weather for your location. 🤔');
})
this.updateStorage();
},
tellJoke(category) {
let vue = this;
let categorySet = false;
if (category !== true && category !== false) {
category = category.trim();
categorySet = true;
} else {
category = 'general'
}
let url = 'https://official-joke-api.appspot.com/jokes/' + category + '/random';
axios.get(url)
.then(function (response) {
console.log(response);
let joke = response.data[0];
vue.botMessage(joke.setup);
setTimeout(() => {
vue.botMessage(joke.punchline);
vue.scrollDown();
}, 3500);
vue.lastMessageData = {
joke: true,
category: category
};
})
.catch(function (error) {
console.log(error);
if (categorySet) {
vue.botMessage("Sorry, i don't know any jokes about this topic.. 🙄");
} else {
vue.botMessage("I can't remember any jokes right now, sorry. 😢");
}
});
this.updateStorage();
},
meMessage(message) {
this.addMessage(this.username + ' ' + message, false, true);
this.react(message, true);
},
clearStorage() {
localStorage.clear();
location.reload();
},
getBootswatchThemes() {
let vue = this;
axios.get('https://bootswatch.com/api/4.json')
.then(function (response) {
vue.themes = response.data.themes;
})
.catch(function (error) {
console.log(error);
})
}
}
})