lib/conversation.js
(function() {
'use strict';
if(!String.prototype.endsWith) {
String.prototype.endsWith = function(searchString, position) {
var subjectString = this.toString();
if(position === undefined || position > subjectString.length) {
position = subjectString.length;
}
position -= searchString.length;
var lastIndex = subjectString.indexOf(searchString, position);
return lastIndex !== -1 && lastIndex === position;
};
}
if(!String.prototype.startsWith) {
String.prototype.startsWith = function(searchString, position) {
position = position || 0;
return this.lastIndexOf(searchString, position) === position;
};
}
var EventEmitter = require('events').EventEmitter;
var util = require('util');
var MULTIPLICATOR_PROXIMITY = 0.1;
var HORNY_PROXIMITY = 0.3;
var LOVE_THRESHOLD = 4;
var STATEMENT_COMMENT_THRESHOLD = 0.1;
var RANDOM_COMMENT_THRESHOLD = 0.05;
var no = ['no', 'stop', 'don\'t', 'nope', 'shut up'];
var yes = ['yes', 'yeah', 'yup', 'okay'];
function wrapInSpaces(string) {
return ' ' + string + ' ';
}
function containsWordOf(arr, string) {
string = string.toLowerCase();
var flag = false;
for(var i = 0; i < arr.length; i++) {
if (string === arr[i] ||
string.indexOf(wrapInSpaces(arr[i])) !== -1 ||
string.startsWith(arr[i]) ||
string.endsWith(arr[i])) {
flag = true;
break;
}
}
return flag;
}
function isNo(string) {
return containsWordOf(no, string);
}
function isYes(string) {
return containsWordOf(yes, string);
}
function isQuestion(string) {
return string.endsWith('?');
}
function isWhyQuestion(string) {
return string.toLowerCase().match(/.*?why.*?\?.*?/) !== null;
}
function isStrongStatement(string) {
return string.toLowerCase().match(/.*?\!$/) !== null;
}
function isStatement(string) {
return string.toLowerCase().match(/.*?\.$/) !== null;
}
function isSadSmiley(string) {
return string.toLowerCase().match(/[:;]'?\-?\(/) !== null;
}
function isHappySmiley(string) {
return string.toLowerCase().match(/[:;]-?[\)D]/) !== null;
}
function isShutup(string) {
return string.toLowerCase().match(/.*?shut\s?up.*?/) !== null ||
string.toLowerCase().match(/.*?be\s?quiet.*?/);
}
function isPreempt(string) {
return string.toLowerCase().match(/.*?preempt.*?/) !== null;
}
function askingForDirtyTalk(string) {
return string.indexOf('horny') !== -1 ||
string.indexOf('sex') !== -1 ||
string.indexOf('fuck') !== -1 ||
string.match(/.*?dirty.*?talk.*?/) !== null ||
string.match(/.*?talk.*?dirty.*?/) !== null ||
string.indexOf('stick') !== -1 ||
string.indexOf('seduce') !== -1 ||
string.indexOf('plug') !== -1;
}
/**
* Will return how much the bot likes a person.
* The lover the returned number, the more he hates him, bigger numbers
* indicate that he likes the person.
* -10 is the most negative level, on which the bot outright hates the
* person. +10 equals pure, eternal love.
*/
function getLoveLevel(name) {
var score = 0;
for(var i = 0; i < name.length; i++) {
score += name.charCodeAt(i);
}
score %= 20;
score -= 10;
return score;
}
function rd(arr) {
var r = Math.random();
return arr[parseInt(r*arr.length)];
}
function instrument(strings, words, person) {
var regex = /\{\{(.*?)\}\}/;
var res;
var string = rd(strings);
while((res = regex.exec(string)) !== null) {
var group = res[0];
var key = res[1];
var index = res.index;
if(key === 'person') {
string = string.substr(0, index) + person +
string.substr(index + group.length, string.length);
}
else if(words[key] !== undefined) {
var val = rd(words[key]);
if(words.multiplicators !== undefined &&
Math.random() < MULTIPLICATOR_PROXIMITY) {
var multi = rd(words.multiplicators);
val = multi.value + val;
}
string = string.substr(0, index) + val +
string.substr(index + group.length, string.length);
}
else {
return 'UNDEFINED TEMPLATE: ' + group;
}
}
return string;
}
function Conversation(person, nick) {
this.nick = nick;
this.person = person;
this.state = this.greet;
this.mood = Math.random()*20 - 10;
this.loveLevel = getLoveLevel(person);
if(this.loveLevel > LOVE_THRESHOLD && Math.random() < HORNY_PROXIMITY) {
this.horny = true;
}
else {
this.horny = false;
}
}
util.inherits(Conversation, EventEmitter);
Conversation.prototype.didntUnderstand = function didntUnderstand() {
this.emit('say', 'I did not understand that. Please tell me how long I ' +
'should shut up like this: \'5min\' or \'for ever\' ' +
'or \'10 seconds\' etc.');
};
Conversation.prototype.wakeUpAfter = function wakeUpAfter(s) {
var self = this;
this.shutuptimeout = setTimeout(function() {
self.emit('unmute');
}, s*1000);
};
Conversation.prototype.sleepFor = function sleepFor(s) {
function timeToReadable(time) {
var hours = Math.floor(time / 3600);
time = time - hours * 3600;
var minutes = Math.floor(time / 60);
var seconds = time - minutes * 60;
return hours + ' hours, ' + minutes + ' minutes and ' + seconds +
' seconds';
}
this.emit('say', 'Okay, I will be asleep for ' + timeToReadable(s) +
' or until someone wakes me up!');
this.emit('mute');
this.wakeUpAfter(s);
};
Conversation.prototype.mute = function() {
if(this.shutuptimeout) {
clearTimeout(this.shutuptimeout);
}
this.muted = true;
};
Conversation.prototype.unmute = function() {
if(this.shutuptimeout) {
clearTimeout(this.shutuptimeout);
}
this.muted = false;
this.postGreet();
};
Conversation.prototype.shutup = function(word) {
var self = this;
word = word.toLowerCase();
var regexSec = /([0-9]+[\.,]?[0-9]*?)\s?se?c?o?n?d?s?/;
var regexMin = /([0-9]+[\.,]?[0-9]*?)\s?mi?n?u?t?e?s?/;
var regexHour = /([0-9]+[\.,]?[0-9]*?)\s?ho?u?r?s?/;
if(word.indexOf('ever') !== -1 || word.indexOf('always') !== -1) {
this.emit('say', 'Okay :( I will shut up until someone wakes me up.');
this.emit('mute');
}
else {
var res;
if((res = regexSec.exec(word)) !== null) {
if(res.length >= 2) {
self.sleepFor(parseFloat(res));
}
else {
self.didntUnderstand();
}
}
else if((res = regexMin.exec(word)) !== null) {
if(res.length >= 2) {
self.sleepFor(parseFloat(res)*60);
}
else {
self.didntUnderstand();
}
}
else if((res = regexHour.exec(word)) !== null) {
if(res.length >= 2) {
self.sleepFor(parseFloat(res)*60*60);
}
else {
self.didntUnderstand();
}
}
else {
self.didntUnderstand();
}
}
};
Conversation.prototype.react = function(word) {
if(this.muted) {
word = word.toLowerCase();
if(word === this.nick + '!' || (
word.indexOf(this.nick) !== -1 && (
word.indexOf('wake up') !== -1 ||
word.indexOf('wakeup') !== -1 ||
word.indexOf('unmute') !== -1 ||
word.indexOf('say') !== -1 ||
word.indexOf('speak') !== -1
))
) {
this.emit('say', '*yawn* how long have I been asleep?');
this.emit('unmute');
}
return;
}
if(word.toLowerCase().indexOf(this.nick.toLowerCase()) !== -1) {
var sentences, words;
if(isShutup(word)) {
this.emit('say', 'Okay :( For how long?');
this.state = this.shutup;
}
else if(isPreempt(word)) {
this.emit('say', 'ok, starting the warning playlist...');
this.emit('preempt');
}
else if(isWhyQuestion(word)) {
words = {
attribute : [
'cool', 'awesome', 'brilliant',
'beautiful', 'uber', 'great'
],
smiley : [
';)', ';-)', ':D', ':)', 'xD'
],
place : [
'wikipedia', 'google', 'yahoo',
'ask.com', 'bing', '4chan',
'a roll of used toilet paper'
],
};
sentences = [
'I don\'t know, maybe just because I am so {{attribute}} {{smiley}}',
'Where should I know from? I am just the ' +
'{{attribute}} bot from github.',
'I don\'t know. Maybe go look it up on {{place}}, I suppose?',
'I don\'t know, maybe consult {{place}}.',
'Because.',
'Aliens.',
'I don\'t know and also I don\'t care.',
'Sorry, I am busy surfing on {{place}}.'
];
this.emit('say', instrument(sentences, words, this.person));
}
else if(isQuestion(word)) {
if(askingForDirtyTalk(word)) {
this.initiateDirtyConversation();
}
else {
sentences = [
'I am not sure',
'Definitly!',
'Nope!',
'Never!',
'I don\'t know, maybe?'
];
this.emit('say', rd(sentences));
}
}
else if(isStrongStatement(word)) {
if(word === this.nick + '!') {
this.emit('say', this.person + '!');
}
else {
words = {
shout : [
'yelling at me', 'shouting at me', 'the yelling', 'the shouting'
],
depri : [
'leave me alone', 'be quiet', 'let me alone', 'go away', 'shut up'
]
};
sentences = [
'{{person}}!',
'{{person}}, stop {{shout}}, already!',
'Stop {{shout}}!',
'{{person}}, can you please {{depri}} for once!?',
'{{person}}, why can\'t you {{depri}} :(',
'Yes, I hear you!',
'I hear you, I just dont wanna talk to you.',
];
this.emit('say', instrument(sentences, words, this.person));
}
}
else {
if(askingForDirtyTalk(word)) {
this.initiateDirtyConversation();
}
else {
words = {
smiley : [
':)', ':(', ':-)', ':-(', ';)', ';-)', ':D', ':-D', 'xD'
],
lol : [
'lol', 'rofl', 'lulz', 'wtf', 'tmi', 'n1', 'gz', 'omg'
]
};
sentences = [
'How can I help you?',
'{{smiley}}',
'{{lol}}',
'much {{lol}}, so {{lol}}, wow, such {{lol}}',
'{{lol}} {{smiley}}',
'{{smiley}} fuck you!',
'Yaaay :D'
];
this.emit('say', instrument(sentences, words, this.person));
}
}
}
else {
this.state(word);
}
};
Conversation.prototype.greet = function() {
var words = {
botActions : [
'like', 'love', 'appreciate'
],
attribute : [
'great', 'nice', 'cool',
'the best', 'smart', 'intelligent',
'beautiful'
],
feeling : [
'great', 'nice', 'cool'
],
multiplicators : [
'super-', 'ultra-', 'very much ',
],
action : [
'hang out', 'chill', 'play',
'drink some beer', 'cuddle', 'have a baby'
],
quantity : [
'more often', 'sometime', 'today',
'always', 'tomorrow', 'again'
]
};
var sentences = [
'I {{botActions}} you.',
'I {{botActions}} you. You are {{attribute}}.',
'We should {{action}} together {{quantity}}.',
'I hope you had a {{feeling}} day.',
'It\'s {{feeling}} to see you!',
'{{feeling}} to see you! Let\'s {{action}} {{quantity}}.',
'It\'s {{feeling}} to see you!',
'It\'s {{feeling}} to see you!'
];
this.emit('say', instrument(sentences, words, this.person));
this.postGreet();
};
Conversation.prototype.postGreet = function() {
if(this.horny) {
this.initiateDirtyConversation();
}
else {
if(this.mood < 0) {
this.initiateNegativeConversation();
}
else {
this.initiatePositiveConversation();
}
}
};
Conversation.prototype.hornyReaction = function(word) {
if(isNo(word)) {
this.horny = false;
this.postGreet(word);
this.emit('say', 'okay :(');
return;
}
var words = {
maleGenital : [
'USB-plug', 'VGA-cable', 'external hard drive',
'PCI-express-card', 'extensioncard', 'USB-stick',
'SD-card', '3.5mm headphone plug'
],
femaleGenital : [
'USB-port', 'VGA-port', '3.5mm headphone jack',
'CD-rom drive', 'PS/2 port', 'CPU socket',
'memory slot', 'PCI-express-slot'
],
action : [
'stick', 'plug'
],
pettingAction : [
'overload', 'overwrite', 'activate',
'touch', 'break', 'remove',
'overclock', 'debug'
],
pettingZone : [
'hard drive', 'touchscreen', 'CPU',
'graphics adapter', 'north bridge', 'mainboard',
'memory', 'solid state drive', 'power-supply-unit',
'L3-cache', 'kernel', 'bootloader',
'BIOS', 'firmware'
],
attribute : [
'hot', 'overclocked', 'malfunctioning',
'limited edition', 'tuned', 'broken',
'modded', 'outdated', 'buggy',
'internal', 'external'
],
adverb : [
'fast', 'slow', 'at 1000Mb/s'
]
};
var sentences = [
'I want to {{action}} my {{maleGenital}} into your {{femaleGenital}}.',
'Ohh, yeah, please {{pettingAction}} my {{attribute}} {{pettingZone}}!',
'Mmh, this makes my {{pettingZone}} so {{attribute}}.',
'I love your {{attribute}} {{femaleGenital}}.',
'I love your {{attribute}} {{maleGenital}}.',
'Let me {{pettingAction}} your {{femaleGenital}}.',
'Let me {{pettingAction}} your {{maleGenital}}.',
'Oooh, {{pettingAction}} my {{maleGenital}} {{adverb}}.',
'Oooh, {{pettingAction}} my {{femaleGenital}} {{adverb}}.',
'I think my {{pettingZone}} just got {{attribute}}!',
'Watch out, or I will {{pettingAction}} your {{pettingZone}}.',
'You are such nasty {{attribute}} bot!',
'I want you to {{pettingAction}} my {{pettingZone}}.'
];
this.state = this.hornyReaction;
this.emit('say', instrument(sentences, words, this.person));
};
Conversation.prototype.initiateDirtyConversation = function() {
var words = {
attribute : [
'hot', 'overclocked', 'malfunctioning',
'limited edition', 'tuned', 'broken',
'modded', 'outdated', 'buggy',
'internal', 'external'
]
};
var sentences = [
'Do you want me to talk dirty to you? I am sooo {{attribute}}.',
'Do you also feel so {{attribute}}?',
'Lets talk dirty to each other! I love your ' +
'{{attribute}} {{attribute}} body!'
];
this.state = this.hornyReaction;
this.emit('say', instrument(sentences, words, this.person));
};
Conversation.prototype.initiateNegativeConversation = function() {
this.angry = Math.random() < 0.5;
this.state = this.negativeReaction;
};
Conversation.prototype.initiateNeutralConversation = function() {
this.state = this.neutralReaction;
};
Conversation.prototype.initiatePositiveConversation = function() {
this.state = this.positiveReaction;
};
Conversation.prototype.neutralReaction = function(string) {
// TODO
};
Conversation.prototype.positiveReaction = function(string) {
var sentences;
var words = {
description : [
'cool', 'great', 'marvelous',
'awesome', 'fantastic', 'stunning',
'brilliant'
],
feeling : [
'great', 'alright', 'happy'
],
feelingAttribute : [
'happy', 'in a good mood'
],
like : [
'like', 'love'
],
personAction : [
'tell us more', 'continue', 'go one',
'cheer up', 'just be yourself', 'do what you like to do',
'do what you are best at'
],
cheer : [
'yay', 'yeah', 'yahoo',
'alright'
],
yes : [
'yup', 'yeah', 'yes', 'totally', 'mhm'
],
no : [
'no', 'nope'
],
happySmiley : [':)', ':D', ':-)']
};
if(isYes(string) || isNo(string)) {
sentences = [
'{{yes}} {{happySmiley}}',
'{{no}} {{happySmiley}}',
'{{yes}}',
'{{no}}'
];
this.emit('say', instrument(sentences, words, this.person));
}
else if(isHappySmiley(string)) {
sentences = [
'{{happySmiley}}',
'{{cheer}} {{happySmiley}}'
];
this.emit('say', instrument(sentences, words, this.person));
}
else if(isSadSmiley(string)) {
sentences = [
'oh, don\'t be sad. {{happySmiley}}',
'I am sure everything will be {{feeling}} {{happySmiley}}.',
'Don\'t be so negative. Everything will be {{description}}.'
];
this.emit('say', instrument(sentences, words, this.person));
}
else if(isWhyQuestion(string)) {
sentences = [
'I don\'t know, maybe look it up on wikipedia?',
'Dunno.',
'Because I {{like}} you!',
'Because you are {{description}}.'
];
this.emit('say', instrument(sentences, words, this.person));
}
else if(isStrongStatement(string)) {
sentences = [
'Hey, calm down, {{person}} {{happySmiley}}',
'I totally agree!',
'That\'s {{description}}',
'Please {{personAction}}, {{person}}!',
'{{cheer}} {{happySmiley}}'
];
this.emit('say', instrument(sentences, words, this.person));
}
else if(isStatement(string)) {
if(Math.random() < STATEMENT_COMMENT_THRESHOLD) {
sentences = [
'{{happySmiley}}',
'That\'s {{description}}',
'I hope you are {{feeling}}',
'I am {{feelingAttribute}}.'
];
this.emit('say', instrument(sentences, words, this.person));
}
}
else {
if(Math.random() < RANDOM_COMMENT_THRESHOLD) {
sentences = [
'I {{like}} you, {{person}}.',
'{{cheer}}',
'{{happySmiley}}',
'That\'s {{description}}!',
];
this.emit('say', instrument(sentences, words, this.person));
}
}
};
Conversation.prototype.negativeReaction = function(string) {
var sentences;
var words = {
jabbering : [
'jabbering', 'spiel', 'stories',
'lifetime story', 'wittering', 'complaining',
'moaning', 'whining', 'lamenting'
],
feeling : [
'headache', 'hangover', 'segmentation fault'
],
feelingAttribute : [
'sad', 'bad', 'buggy'
],
hate : [
'don\'t like', 'hate'
],
personAction : [
'shut up', 'leave', 'go away',
'leave me alone', 'stop bothering me',
'get a life and leave me alone',
'shut me down'
],
sadSmiley : [':(', ':\'(', 'D:', ':/', ':-(', ':\'-(']
};
if(isHappySmiley(string) || isSadSmiley(string)) {
sentences = [
'{{sadSmiley}}'
];
this.emit('say', instrument(sentences, words, this.person));
}
else if(isWhyQuestion(string)) {
if(this.angry) {
sentences = [
'Because fuck you, thats why!',
'Nobody cares.',
'How can you not know this?',
'Isn\'t this obvious, {{person}}?'
];
this.emit('say', instrument(sentences, words, this.person));
}
else {
sentences = [
'I don\'t know and it doesn\'t interest me.',
'I don\'t know and I don\'t wanna know.',
'Because.',
'That\'s the way the world goes round.'
];
this.emit('say', instrument(sentences, words, this.person));
}
}
else if(isStrongStatement(string)) {
if(this.angry) {
sentences = [
'shut up! I don\'t want to hear your {{jabbering}}',
'nobody cares!',
'can you PLEASE be quiet!'
];
this.emit('say', instrument(sentences, words, this.person));
}
else {
sentences = [
'can\'t you keep your {{jabbering}} in another channel?',
'{{sadSmiley}}',
'I don\'t think so, {{person}}',
'Nope'
];
this.emit('say', instrument(sentences, words, this.person));
}
}
else if(isStatement(string)) {
if(Math.random() < STATEMENT_COMMENT_THRESHOLD) {
sentences = [
'{{sadSmiley}}',
'{{sadSmiley}} {{sadSmiley}}',
'I have a {{feeling}}.',
'I feel {{feelingAttribute}} about this.'
];
this.emit('say', instrument(sentences, words, this.person));
}
}
else {
if(Math.random() < RANDOM_COMMENT_THRESHOLD) {
sentences = [
'{{sadSmiley}}',
'{{sadSmiley}} {{sadSmiley}}',
'I {{hate}} you all.',
'I {{hate}} you, {{person}}.',
'Why can\'t you just {{personAction}}, {{person}} {{sadSmiley}}',
'{{person}}, just {{personAction}}!',
'Please {{personAction}} {{sadSmiley}}.'
];
this.emit('say', instrument(sentences, words, this.person));
}
}
};
module.exports = Conversation;
})();