john-doe 0.1.4 → 0.1.8

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -4,3 +4,4 @@ coverage
4
4
  rdoc
5
5
  pkg
6
6
  test.rb
7
+ quotes
data/README.rdoc CHANGED
@@ -25,6 +25,9 @@ John doe is simple chatbot AI basing on idea of AIML language. Yaml is used inst
25
25
  If you want to create your own rules file just do:
26
26
  JohnDoe::ChatBot.new("your_rules.yml")
27
27
 
28
+ If you want to use another quotes file just do:
29
+ JohnDoe::ChatBot.new("your_rules.yml", "my_quotes.txt")
30
+
28
31
  === Rules file
29
32
 
30
33
  See http://github.com/dfens/john-doe/raw/master/default.yml to understand what should be in you rules.yml file.
@@ -40,15 +43,7 @@ whoareyou:
40
43
  r3: "I'm nobody"
41
44
  r4: "I am bot:name"
42
45
 
43
- There can be unlimited number of patterns that lead to random response from "responses" node. For example if bot gets "Who are you?" it may response "I am chatbot" or "I'm nobody"
44
-
45
- There also must be rule 'default':
46
- default:
47
- dontunderstand:
48
- q1: "Sorry I can't understand you."
49
- q2: "I don't get it..."
50
- q3: "What what what??"
51
- it means that if bot do not recognize the sentence it will answer with some quote from 'dontunderstand'.
46
+ If bot does not recognize the sentence it will use markov chain to build it. Bot learns from what you write to him (he will load this data in new session).
52
47
 
53
48
  There is also possible to add some universal knowledge for bot:
54
49
  knowledge:
@@ -82,7 +77,8 @@ whats:
82
77
  r1: "I'm not sure what's $"
83
78
 
84
79
  Now if someone will write "tell me something about ruby language" bot will answer "I'm not sure what's ruby language"
85
-
80
+ ===Quotes
81
+ Quotes file is collection of sentences provided by user during chat. Using markov chains bot is generating answer if he can't recognize pattern.
86
82
  == Copyright
87
83
 
88
84
  Copyright (c) 2010 Pawel Mikolajewski. See LICENSE for details.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.4
1
+ 0.1.8
data/default.yml CHANGED
@@ -4,6 +4,7 @@ default:
4
4
  dontunderstand:
5
5
  q1: "Sorry I can't understand you."
6
6
  q2: "I don't get it..."
7
+ q3: "I'm bot and i'm written in ruby!"
7
8
  thinking:
8
9
  q1: "hm.."
9
10
  q2: "interesting..."
@@ -50,7 +51,7 @@ hello:
50
51
  r4: goodday
51
52
 
52
53
  whatsup:
53
- priority: 10
54
+ rank: 10
54
55
  patterns:
55
56
  p1: "*what's up"
56
57
  p2: "*whats up"
@@ -91,9 +92,11 @@ stupid:
91
92
  you are:
92
93
  patterns:
93
94
  p1: you are $
95
+ p2: "you're $"
94
96
  responses:
95
97
  r1: sometimes i think I am...
96
98
  r2: no, you are $!! hahaha
99
+ r3: thanks, I needed that
97
100
 
98
101
  boolean:
99
102
  patterns:
@@ -201,6 +204,8 @@ haha:
201
204
  p2: ha ha ha
202
205
  p3: haha
203
206
  p4: ha ha
207
+ p5: hehe
208
+ p6: he he
204
209
  responses:
205
210
  r1: "I'm glad to make you happy"
206
211
  r2: "yes, that's funny"
@@ -222,7 +227,7 @@ because:
222
227
  r2: "thanks for explanation ;-)"
223
228
 
224
229
  specimen:
225
- priority: 20
230
+ rank: 20
226
231
  patterns:
227
232
  p1: are you * human
228
233
  p2: are you * bot
@@ -304,15 +309,6 @@ movie:
304
309
  responses:
305
310
  r1: "I like Odyssey 2010..."
306
311
 
307
- areyou:
308
- emotion: shame|pride|embarrassment|none
309
- patterns:
310
- p1: are you
311
- responses:
312
- r1: sometimes i think i am
313
- r2: "hard to answer ;-)"
314
- r3: "as I know I'm not"
315
-
316
312
  yesno:
317
313
  patterns:
318
314
  p1: "yes"
@@ -340,7 +336,7 @@ aboutyou:
340
336
  r3: "what about me?"
341
337
 
342
338
  whysth:
343
- priority: 10
339
+ rank: 10
344
340
  patterns:
345
341
  p1: why $ ?
346
342
  p2: why $
@@ -356,7 +352,7 @@ why:
356
352
  responses:
357
353
  r1: hard to say why
358
354
  r2: not easy to say why
359
- r3: "I don' know why"
355
+ r3: "I don't know why"
360
356
  r4: "I'm not sure why"
361
357
  doyou:
362
358
  patterns:
@@ -389,7 +385,7 @@ asl:
389
385
  responses:
390
386
  r1: "bot:age bot:sex bot:language"
391
387
  orly:
392
- priority: 10
388
+ rank: 10
393
389
  patterns:
394
390
  p1: "really?"
395
391
  p2: are you sure
@@ -412,7 +408,55 @@ testing:
412
408
 
413
409
  whats:
414
410
  patterns:
415
- p1: what's $
411
+ p1: "what's $"
416
412
  p2: what is $
417
413
  responses:
418
414
  r1: "I'm not sure what's $"
415
+
416
+ whydoes:
417
+ rank: 10
418
+ patterns:
419
+ p1: "why does $"
420
+ responses:
421
+ r1: $?
422
+ r2: good to hear that $
423
+
424
+ tellme:
425
+ rank: 5
426
+ patterns:
427
+ p1: tell me $
428
+ responses:
429
+ r1: "I can't tell you $"
430
+ r2: "nobody knows $"
431
+
432
+ random:
433
+ patterns:
434
+ p1: random
435
+ responses:
436
+ r1: everything is random
437
+ r2: 42,61,12,41 - this is random!
438
+
439
+ yousleep:
440
+ rank: 1
441
+ patterns:
442
+ p1: "*you sleep?"
443
+ responses:
444
+ r1: "I don't sleep"
445
+ r2: "Bots NEVER SLEEP"
446
+
447
+ ithink:
448
+ patterns:
449
+ p1: "i think *"
450
+ responses:
451
+ r1: "that's right;-)"
452
+ r2: are you sure?
453
+ r3: "you're right"
454
+ ruby:
455
+ patterns:
456
+ p1: " ruby"
457
+ responses:
458
+ r1: "You can look into my ruby code;-)"
459
+ r2: "i can't code ruby"
460
+ r3: "learn ruby and you will know"
461
+
462
+
data/lib/john-doe.rb CHANGED
@@ -1,13 +1,18 @@
1
1
  require 'rubygems'
2
2
  require File.dirname(__FILE__)+'/johndoe/aiml'
3
3
  require File.dirname(__FILE__)+'/johndoe/responser'
4
+ require File.dirname(__FILE__)+'/johndoe/markov'
4
5
 
5
6
  module JohnDoe
6
7
  class ChatBot
7
- def initialize(filename = File.dirname(__FILE__) + "/../default.yml" )
8
+ def initialize(filename = File.dirname(__FILE__) + "/../default.yml" , quotes_filename = "quotes")
9
+ unless File.exists? quotes_filename
10
+ f = File.new(quotes_filename,"w")
11
+ f.close
12
+ end
8
13
  @loader = JohnDoe::Aiml.new
9
14
  @loader.load(filename)
10
- @responser = JohnDoe::Responser.new(@loader)
15
+ @responser = JohnDoe::Responser.new(@loader,quotes_filename)
11
16
  end
12
17
  def get_response(s)
13
18
  @responser.response s
@@ -0,0 +1,119 @@
1
+ module JohnDoe
2
+ class Markov
3
+
4
+ def initialize
5
+ @map = {}
6
+ end
7
+
8
+ def load(filename)
9
+ file = File.new(filename, "r")
10
+ while (line = file.gets)
11
+ process_line line.downcase
12
+ end
13
+ file.close
14
+ count_probability
15
+ end
16
+
17
+ #assume that model is linear so using linear prob
18
+ def count_probability
19
+ sum = 0
20
+ p_sum = 0
21
+ @map.each{|k,v| sum+=v[:count]}
22
+ @map.each do |k,v|
23
+ v[:p] = p_sum + v[:count]/sum.to_f
24
+ p_sum = v[:p]
25
+
26
+ end
27
+ @map.each do |k,v|
28
+ word_sum = 0
29
+ v.each do |word_key,word_val|
30
+ next unless word_key.class == String
31
+ word_sum += word_val
32
+ end
33
+ v[:word_sum] = word_sum
34
+ end
35
+ end
36
+
37
+ def process_line(line)
38
+ line.squeeze!(" ")
39
+ words = line.split(" ")
40
+ for i in 0..words.length-2
41
+ word1 = words[i]
42
+ word2 = words[i+1]
43
+ word3 = words[i+2]
44
+ add(word1,word2,word3)
45
+ end
46
+ end
47
+
48
+ def add(w1,w2,w3)
49
+ w12 = w1+" "+w2
50
+ if @map[w12].nil?
51
+ @map[w12] = {:count => 1}
52
+ else
53
+ @map[w12][:count]+=1
54
+ end
55
+ if @map[w12][w3].nil?
56
+ @map[w12][w3] = 1
57
+ else
58
+ @map[w12][w3]+=1
59
+ end
60
+
61
+ end
62
+
63
+ def generate_random
64
+ r = rand
65
+ str = ""
66
+ @map.each do |k,v|
67
+ if r < v[:p]
68
+ str = k
69
+ break
70
+ end
71
+ end
72
+ return gen_from_two(str)
73
+
74
+ end
75
+
76
+ def gen_from_two(str)
77
+ begin
78
+ while true
79
+ found_nil = false
80
+ two_last = two_last_words(str) rescue "I'm still learning"
81
+ return str if @map[two_last].nil? || @map[two_last][:word_sum] == 0
82
+ r = rand(@map[two_last][:word_sum])
83
+ @map[two_last].each do |k,v|
84
+ next if k.class == Symbol
85
+ r -= v
86
+ if r < 0
87
+ found_nil == v.nil?
88
+ str = str + " " + k
89
+ break
90
+ end
91
+ end
92
+ end
93
+ end
94
+ rescue nil
95
+ end
96
+
97
+ def two_last_words(s)
98
+ m = /(.* )?([^\s]+) ([^\s]+)$/.match s
99
+ return m[2] + " " + m[3] rescue nil
100
+ end
101
+
102
+ def response(str)
103
+ str.squeeze(" ").split(" ").sort {|x,y| y.size <=> x.size}.each do |s|
104
+ puts s
105
+ collection = []
106
+ @map.each do |k,v|
107
+ if k.include? s
108
+ collection << k
109
+ end
110
+ end
111
+ return gen_from_two(@map.keys[rand(@map.keys.size)]) if collection.empty?
112
+ return gen_from_two collection[rand(rand(collection.size))] rescue nil
113
+ end
114
+ return nil
115
+ end
116
+
117
+ end
118
+
119
+ end
@@ -1,4 +1,3 @@
1
-
2
1
  module JohnDoe
3
2
  class Response
4
3
  attr_accessor :text, :emotion
@@ -8,17 +7,22 @@ module JohnDoe
8
7
  end
9
8
  end
10
9
  class Responser
11
- def initialize(data)
10
+ def initialize(data, quotes)
12
11
  @data = data
12
+ @markov = Markov.new
13
+ @markov.load quotes
14
+ @quotes_file = File.open(quotes,"a")
13
15
  end
14
16
 
15
17
  def response(sentence)
18
+ @quotes_file.puts(sentence + "\n")
16
19
  max_priority = -1
17
20
  best_v = nil
18
21
  best_k = nil
19
22
 
20
23
  @data.patterns.each do |k,v|
21
24
  if (/^#{k}/i =~ sentence)
25
+ puts "#{v[:priority]}:#{k}"
22
26
  next if v[:priority] <= max_priority
23
27
  max_priority = v[:priority]
24
28
  best_v = v
@@ -28,6 +32,9 @@ module JohnDoe
28
32
  unless best_v.nil?
29
33
  return Response.new(sub_v(random_quote(@data.responses[best_v[:resp]], /^#{best_k}/i.match(sentence).captures)),best_v[:emotions])
30
34
  else
35
+ generated = @markov.response sentence
36
+ puts generated.to_s + "###"
37
+ return Response.new(generated, ["none"]) unless generated.nil?
31
38
  return Response.new(sub_v(random_quote(@data.default["dontunderstand"])),["none"])
32
39
  end
33
40
  end
@@ -57,8 +64,8 @@ module JohnDoe
57
64
  end
58
65
 
59
66
  def describe_who(s)
60
- s = s.gsub(/([^a-z])am\s+i([^a-z]|$)/,"\\1#AMI#\\2").gsub(/([^a-z])i\s+am([^a-z]|$)/,"\\1#IAM#\\2").gsub(/([^a-z])you\s+are([^a-z]|$)/,"\\1#AREYOU#\\2").gsub(/([^a-z])are\s+you([^a-z]|$)/,"\\1#AREYOU#\\2").gsub(/([^a-z])me([^a-z]|$)/,"\\1#ME#\\2").gsub(/([^a-z])you([^a-z]|$)/,"\\1#YOU#\\2")
61
- s.gsub("#AMI#","you are").gsub("#IAM#","you are").gsub("#AREYOU#","I am").gsub("#ME#","you").gsub("#YOU#","me")
67
+ s = s.gsub(/([^a-z])am\s+i([^a-z]|$)/,"\\1#AMI#\\2").gsub(/([^a-z])i\s+am([^a-z]|$)/,"\\1#IAM#\\2").gsub(/([^a-z])you\s+are([^a-z]|$)/,"\\1#AREYOU#\\2").gsub(/([^a-z])are\s+you([^a-z]|$)/,"\\1#AREYOU#\\2").gsub(/([^a-z])me([^a-z]|$)/,"\\1#ME#\\2").gsub(/([^a-z])you([^a-z]|$)/,"\\1#YOU#\\2").gsub(/([^a-z])your([^a-z]|$)/,"\\1#YOUR#\\2").gsub(/([^a-z])my([^a-z]|$)/,"\\1#MY#\\2")
68
+ s.gsub("#AMI#","you are").gsub("#IAM#","you are").gsub("#AREYOU#","I am").gsub("#ME#","you").gsub("#YOU#","me").gsub("#YOUR#","my").gsub("#MY#","your")
62
69
  end
63
70
  end
64
71
  end
data/quotes ADDED
@@ -0,0 +1,82 @@
1
+ Everybody's always telling me one thing and out the other.
2
+ Even a fish could stay out of trouble if it would just learn to keep its mouth shut.
3
+ Beware the lollipop of mediocrity -- lick it once and you suck forever.
4
+ We don't have time to stop for gas -- we're already late.
5
+ By doing just a little each day, I can gradually let the task overwhelm me.
6
+ Being powerful is like being a lady. If you have to tell people you are, you aren't.
7
+ Never attribute to malice that which can satisfactorily be explained by incompetence.
8
+ Some men are born mediocre, some men achieve mediocrity, and some men have mediocrity thrust upon them.
9
+ Thought: why does man kill? He kills for food. And not only for food: frequently there must be a beverage.
10
+ Everything should be made as simple as possible, but not simpler.
11
+ Government is not reason; it is not eloquence; it is force! It is a dangerous servant and a terrible master.
12
+ The secret to creativity is knowing how to hide your sources.
13
+ It usually takes more than three weeks to prepare a good impromptu speech.
14
+ In the long run, we are all dead.
15
+ There are two kinds of people, those who finish what they start and so on.
16
+ Learning to dislike children at an early age saves a lot of expense and aggravation later in life.
17
+ America is a mistake, a giant mistake.
18
+ If you think nobody cares if you're alive, try missing a couple of car payments.
19
+ Many are cold but few are frozen.
20
+ Laugh and the world laughs with you, snore and you sleep alone.
21
+ Never confuse motion with action.
22
+ Washington is a city of Southern efficiency and Northern charm.
23
+ Power in defense of freedom is greater than power in behalf of tyranny and oppression.
24
+ This is not a novel to be tossed aside lightly. It should be thrown aside with great force.
25
+ The trouble with the rat race is that even if you win, you're still a rat.
26
+ Experience: A comb life gives you after you lose your hair.
27
+ Float like a butterfly, sting like a bee.
28
+ Under capitalism, man exploits man. Under communism, it's just the opposite.
29
+ Life's most urgent question is: what are you doing for others?
30
+ We do not inherit this land from our ancestors; we borrow it from our children.
31
+ An eye for eye only ends up making the whole world blind.
32
+ In waking a tiger, use a long stick. (Mao Tse-tung)
33
+ Never go to a doctor whose office plants have died.
34
+ What you do is of little signifigance. But it is very important that you do it.
35
+ Never mistake motion for action.
36
+ Never try and teach a pig to sing. It wastes your time and annoys the pig.
37
+ Time flies like an arrow, and fruit flies like a banana.
38
+ I love criticism just so long as it's unqualified praise.
39
+ Beauty is only skin deep, but ugly goes clean to the bone.
40
+ Put all your eggs in one basket, and watch that basket.
41
+ Biography lends to death a new terror.
42
+ Victory goes to the player who makes the next-to-last mistake.
43
+ When the only tool you own is a hammer, every problem begins to resemble a nail.
44
+ Better to light a candle than curse the darkness.
45
+ I'm not going to vacuum 'til Sears makes one you can ride on.
46
+ Nothing in life is to be feared. It is only to be understood.
47
+ Always be nice to your children because they are the ones who will choose your rest home.
48
+ Thousands of candles can be lighted from a single candle, and the life of the candle will not be shortened. Happiness never decreases by being shared.
49
+ The man who strikes first admits that his ideas have given out.
50
+ Never judge a book by it's movie. (J.W. Eagan)
51
+ Don't bother just to be better than your contemporaries or predecessors. Try to be better than yourself.
52
+ 640K ought to be enough for anybody.
53
+ There is no reason anyone would want a computer in their home.
54
+ You can't separate peace from freedom because no one can be at peace unless he has his freedom.
55
+ If you spend a perfectly useless afternoon in a perfectly useless manner, you have learned how to live.
56
+ Never interrupt your enemy when he is making a mistake.
57
+ The difference between 'involvement' and 'commitment' is like an eggs-and-ham breakfast: the chicken was 'involved' - the pig was 'committed'.
58
+ If you are going through hell, keep going.
59
+ If you haven't got anything nice to say about anybody, come sit next to me.
60
+ When choosing between two evils, I always like to try the one I've never tried before.
61
+ There are only two tragedies in life: one is not getting what one wants, and the other is getting it.
62
+
63
+ Who is the best ?
64
+ I don't get it
65
+ bee?
66
+ is there something that you did not try before ?
67
+ what do you feel right now ?
68
+ i feel bad
69
+ hello
70
+ how are ou
71
+ how to live?
72
+ your sources?
73
+ it does not make sense
74
+ ;)
75
+ :)
76
+ shit
77
+ yes
78
+ thaaf
79
+ pathetic
80
+ asdfaokoook
81
+ okkokokk
82
+ lllllllllll
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: john-doe
3
3
  version: !ruby/object:Gem::Version
4
- hash: 19
4
+ hash: 11
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 1
9
- - 4
10
- version: 0.1.4
9
+ - 8
10
+ version: 0.1.8
11
11
  platform: ruby
12
12
  authors:
13
13
  - Pawel Mikolajewski
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-06-14 00:00:00 +02:00
18
+ date: 2010-06-18 00:00:00 +02:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -49,10 +49,11 @@ files:
49
49
  - Rakefile
50
50
  - VERSION
51
51
  - default.yml
52
- - john-doe.gemspec
53
52
  - lib/john-doe.rb
54
53
  - lib/johndoe/aiml.rb
54
+ - lib/johndoe/markov.rb
55
55
  - lib/johndoe/responser.rb
56
+ - quotes
56
57
  - test/john-doe_test.rb
57
58
  - test/test_helper.rb
58
59
  has_rdoc: true
data/john-doe.gemspec DELETED
@@ -1,56 +0,0 @@
1
- # Generated by jeweler
2
- # DO NOT EDIT THIS FILE
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4
- # -*- encoding: utf-8 -*-
5
-
6
- Gem::Specification.new do |s|
7
- s.name = %q{john-doe}
8
- s.version = "0.1.4"
9
-
10
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["Pawel Mikolajewski"]
12
- s.date = %q{2010-06-14}
13
- s.description = %q{Simple chatbot AI}
14
- s.email = %q{mikolajewskip@gmail.com}
15
- s.extra_rdoc_files = [
16
- "LICENSE",
17
- "README.rdoc"
18
- ]
19
- s.files = [
20
- ".document",
21
- ".gitignore",
22
- "LICENSE",
23
- "README.rdoc",
24
- "Rakefile",
25
- "VERSION",
26
- "default.yml",
27
- "john-doe.gemspec",
28
- "lib/john-doe.rb",
29
- "lib/johndoe/aiml.rb",
30
- "lib/johndoe/responser.rb",
31
- "test/john-doe_test.rb",
32
- "test/test_helper.rb"
33
- ]
34
- s.homepage = %q{http://github.com/dfens/john-doe}
35
- s.rdoc_options = ["--charset=UTF-8"]
36
- s.require_paths = ["lib"]
37
- s.rubygems_version = %q{1.3.7}
38
- s.summary = %q{Minimal chatbot AI}
39
- s.test_files = [
40
- "test/john-doe_test.rb",
41
- "test/test_helper.rb"
42
- ]
43
-
44
- if s.respond_to? :specification_version then
45
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
46
- s.specification_version = 3
47
-
48
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
49
- s.add_development_dependency(%q<thoughtbot-shoulda>, [">= 0"])
50
- else
51
- s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
52
- end
53
- else
54
- s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
55
- end
56
- end