chatterbot 1.0.2 → 2.0.0.pre

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +2 -2
  3. data/LICENSE.txt +18 -9
  4. data/README.markdown +83 -65
  5. data/bin/chatterbot-register +0 -1
  6. data/chatterbot.gemspec +3 -10
  7. data/examples/class_bot.rb +0 -1
  8. data/examples/echoes_bot.rb +2 -2
  9. data/examples/search_bot.rb +1 -1
  10. data/examples/streaming_bot.rb +21 -15
  11. data/lib/chatterbot.rb +7 -12
  12. data/lib/chatterbot/blocklist.rb +61 -0
  13. data/lib/chatterbot/bot.rb +118 -13
  14. data/lib/chatterbot/client.rb +52 -20
  15. data/lib/chatterbot/config.rb +92 -215
  16. data/lib/chatterbot/config_manager.rb +49 -0
  17. data/lib/chatterbot/direct_messages.rb +46 -0
  18. data/lib/chatterbot/dsl.rb +157 -78
  19. data/lib/chatterbot/followers.rb +4 -0
  20. data/lib/chatterbot/handler.rb +29 -0
  21. data/lib/chatterbot/helpers.rb +14 -3
  22. data/lib/chatterbot/home_timeline.rb +5 -8
  23. data/lib/chatterbot/logging.rb +0 -17
  24. data/lib/chatterbot/profile.rb +0 -1
  25. data/lib/chatterbot/reply.rb +6 -11
  26. data/lib/chatterbot/retweet.rb +2 -6
  27. data/lib/chatterbot/safelist.rb +33 -0
  28. data/lib/chatterbot/search.rb +26 -16
  29. data/lib/chatterbot/skeleton.rb +7 -38
  30. data/lib/chatterbot/streaming.rb +26 -33
  31. data/lib/chatterbot/tweet.rb +0 -1
  32. data/lib/chatterbot/ui.rb +9 -2
  33. data/lib/chatterbot/utils.rb +13 -0
  34. data/lib/chatterbot/version.rb +1 -1
  35. data/spec/blocklist_spec.rb +170 -0
  36. data/spec/bot_spec.rb +83 -8
  37. data/spec/client_spec.rb +61 -7
  38. data/spec/config_manager_spec.rb +59 -0
  39. data/spec/config_spec.rb +33 -158
  40. data/spec/direct_messages_spec.rb +115 -0
  41. data/spec/dsl_spec.rb +95 -53
  42. data/spec/handler_spec.rb +27 -0
  43. data/spec/helpers_spec.rb +7 -11
  44. data/spec/home_timeline_spec.rb +42 -31
  45. data/spec/logging_spec.rb +0 -34
  46. data/spec/reply_spec.rb +10 -34
  47. data/spec/search_spec.rb +65 -6
  48. data/spec/spec_helper.rb +25 -1
  49. data/spec/streaming_spec.rb +56 -58
  50. data/spec/whitelist_spec.rb +10 -10
  51. data/specs.watchr +2 -4
  52. data/templates/skeleton.txt +148 -12
  53. metadata +20 -22
  54. data/bin/chatterbot-blacklist +0 -65
  55. data/bin/chatterbot-status +0 -55
  56. data/examples/loop_bot.rb +0 -44
  57. data/lib/chatterbot/blacklist.rb +0 -61
  58. data/lib/chatterbot/db.rb +0 -79
  59. data/lib/chatterbot/streaming_handler.rb +0 -96
  60. data/lib/chatterbot/whitelist.rb +0 -32
  61. data/spec/blacklist_spec.rb +0 -116
  62. data/spec/db_spec.rb +0 -53
  63. data/spec/streaming_handler_spec.rb +0 -78
@@ -13,7 +13,6 @@ module Chatterbot
13
13
  debug "I'm in debug mode, otherwise I would tweet: #{txt}"
14
14
  else
15
15
  debug txt
16
- log txt, original
17
16
  client.update txt, params
18
17
  end
19
18
  rescue Twitter::Error::Forbidden => e
data/lib/chatterbot/ui.rb CHANGED
@@ -4,23 +4,30 @@ module Chatterbot
4
4
  # routines for outputting setup instructions
5
5
  #
6
6
  module UI
7
-
7
+ # Where to send users who need to get API keys
8
8
  API_SIGNUP_URL = "https://twitter.com/apps/new"
9
9
 
10
10
 
11
+ #:nocov:
11
12
  def red(str)
12
13
  puts str.colorize(:red)
13
14
  end
14
15
 
16
+ #:nocov:
15
17
  def green(str)
16
18
  puts str.colorize(:green)
17
19
  end
20
+
21
+ # print out a deprecation notice
22
+ def deprecated(msg, src)
23
+ red(msg)
24
+ green("Called from " + src)
25
+ end
18
26
 
19
27
  #
20
28
  # print out a message about getting a PIN from twitter, then output
21
29
  # the URL the user needs to visit to authorize
22
30
  #
23
- #:nocov:
24
31
  def get_oauth_verifier
25
32
  green "****************************************"
26
33
  green "****************************************"
@@ -1,9 +1,22 @@
1
1
  module Chatterbot
2
+
3
+ # Assorted handy utility methods
2
4
  module Utils
5
+
6
+ #
7
+ # return the id of a tweet, or the incoming object (likely already
8
+ # an ID)
9
+ #
10
+ # @param [Tweet] t the Tweet
3
11
  def id_from_tweet(t)
4
12
  t.is_a?(Twitter::Tweet) ? t.id : t
5
13
  end
6
14
 
15
+ #
16
+ # return the id of a User, or the incoming object (likely already
17
+ # an ID)
18
+ #
19
+ # @param [User] u the User
7
20
  def id_from_user(u)
8
21
  u.is_a?(Twitter::User) ? u.id : u
9
22
  end
@@ -1,3 +1,3 @@
1
1
  module Chatterbot
2
- VERSION = "1.0.2"
2
+ VERSION = "2.0.0.pre"
3
3
  end
@@ -0,0 +1,170 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "Chatterbot::Blocklist" do
4
+ before(:each) do
5
+ @bot = test_bot
6
+ end
7
+
8
+ it "has a list of excluded phrases" do
9
+ @bot.exclude = ["junk", "i hate bots", "foobar", "spam"]
10
+ expect(@bot.skip_me?("did you know that i hate bots?")).to eq(true)
11
+ end
12
+
13
+ describe "skip_me?" do
14
+ before(:each) do
15
+ @bot.exclude = ["junk", "i hate bots", "foobar", "spam"]
16
+ end
17
+
18
+ it "blocks tweets with phrases we don't want" do
19
+ expect(@bot.skip_me?("did you know that i hate bots?")).to eq(true)
20
+ end
21
+
22
+ it "isn't case-specific" do
23
+ expect(@bot.skip_me?("DID YOU KNOW THAT I HATE BOTS?")).to eq(true)
24
+ end
25
+
26
+ it "allows users we do want" do
27
+ expect(@bot.skip_me?("a tweet without any bad content")).to eq(false)
28
+ end
29
+
30
+ it "works with Tweet objects" do
31
+ expect(@bot.skip_me?(Twitter::Tweet.new(:id => 1, :text => "did you know that i hate bots?"))).to eq(true)
32
+ expect(@bot.skip_me?(Twitter::Tweet.new(:id => 1, :text => "a tweet without any bad content"))).to eq(false)
33
+ end
34
+ end
35
+
36
+ describe "on_blocklist?" do
37
+ before(:each) do
38
+ @bot.blocklist = ["skippy", "blippy", "dippy"]
39
+ end
40
+
41
+ it "blocks users we don't want" do
42
+ expect(@bot.on_blocklist?("skippy")).to eq(true)
43
+ end
44
+
45
+ it "allows users we do want" do
46
+ expect(@bot.on_blocklist?("flippy")).to eq(false)
47
+ end
48
+
49
+ it "isn't case-specific" do
50
+ expect(@bot.on_blocklist?("FLIPPY")).to eq(false)
51
+ expect(@bot.on_blocklist?("SKIPPY")).to eq(true)
52
+ end
53
+
54
+ it "works with result hashes" do
55
+ expect(@bot.on_blocklist?(Twitter::Tweet.new(:id => 1,
56
+ :user => {:id => 1, :screen_name => "skippy"}))).to eq(true)
57
+ expect(@bot.on_blocklist?(Twitter::Tweet.new(:id => 1,
58
+ :user => {:id => 1, :screen_name => "flippy"}))).to eq(false)
59
+ end
60
+
61
+ it "works with users" do
62
+ expect(@bot.on_blocklist?(Twitter::User.new({:id => 1, :name => "skippy"}))).to eql(true)
63
+ expect(@bot.on_blocklist?(Twitter::User.new({:id => 1, :name => "flippy"}))).to eql(false)
64
+ end
65
+ end
66
+
67
+ describe "interact_with_user?" do
68
+ before(:each) do
69
+ @user = Twitter::User.new({:id => 1, :name => "flippy"})
70
+ end
71
+
72
+ it "is always true if only_interact_with_followers == false" do
73
+ @bot.only_interact_with_followers = false
74
+ expect(@bot.interact_with_user?(@user)).to eql(true)
75
+ end
76
+
77
+ context "when only_interact_with_followers == true" do
78
+ before(:each) do
79
+ @bot.only_interact_with_followers = true
80
+ @auth_user = double(Twitter::User)
81
+ allow(@bot).to receive(:authenticated_user).and_return(@auth_user)
82
+ end
83
+
84
+ it "is true if follower of account" do
85
+ expect(@bot.client).to receive(:friendship?).with(@user, @auth_user).and_return(true)
86
+ expect(@bot.interact_with_user?(@user)).to eql(true)
87
+ end
88
+
89
+ it "is false if not follower of account" do
90
+ expect(@bot.client).to receive(:friendship?).with(@user, @auth_user).and_return(false)
91
+ expect(@bot.interact_with_user?(@user)).to eql(false)
92
+ end
93
+ end
94
+ end
95
+
96
+
97
+ describe "valid_tweet?" do
98
+ context "with safelist" do
99
+ before(:each) do
100
+ @bot.safelist = ["flippy"]
101
+ end
102
+
103
+ it "is true if on safelist" do
104
+ @user = Twitter::User.new({:id => 1, :name => "flippy"})
105
+ expect(@bot.valid_tweet?(@user)).to eql(true)
106
+
107
+ @tweet = Twitter::Tweet.new(:id => 1, :user => {:id => 1, :screen_name => "flippy"})
108
+ expect(@bot.valid_tweet?(@tweet)).to eq(true)
109
+ end
110
+
111
+ it "is false if not on safelist" do
112
+ @user = Twitter::User.new({:id => 1, :name => "skippy"})
113
+ expect(@bot.valid_tweet?(@user)).to eql(false)
114
+
115
+
116
+ @tweet = Twitter::Tweet.new(:id => 1, :user => {:id => 1, :screen_name => "skippy"})
117
+ expect(@bot.valid_tweet?(@tweet)).to eq(false)
118
+ end
119
+ end
120
+
121
+ context "without safelist" do
122
+ # !skippable_retweet?(object) &&
123
+ #! on_blocklist?(object) &&
124
+ #! skip_me?(object) &&
125
+ #interact_with_user?(object)
126
+ before(:each) do
127
+ @tweet = Twitter::Tweet.new(:id => 1, :user => {:id => 1, :screen_name => "skippy"})
128
+ end
129
+
130
+ it "is false if skippable_retweet" do
131
+ allow(@bot).to receive(:skippable_retweet?).with(@tweet).and_return(true)
132
+ expect(@bot.valid_tweet?(@tweet)).to eql(false)
133
+ end
134
+
135
+ it "is false if on blocklist" do
136
+ allow(@bot).to receive(:skippable_retweet?).with(@tweet).and_return(false)
137
+ allow(@bot).to receive(:on_blocklist?).with(@tweet).and_return(true)
138
+
139
+ expect(@bot.valid_tweet?(@tweet)).to eql(false)
140
+ end
141
+
142
+ it "is false if on exclude list" do
143
+ allow(@bot).to receive(:skippable_retweet?).with(@tweet).and_return(false)
144
+ allow(@bot).to receive(:on_blocklist?).with(@tweet).and_return(false)
145
+ allow(@bot).to receive(:skip_me?).with(@tweet).and_return(true)
146
+
147
+ expect(@bot.valid_tweet?(@tweet)).to eql(false)
148
+ end
149
+
150
+ it "is false if ! interact_with_user" do
151
+ allow(@bot).to receive(:skippable_retweet?).with(@tweet).and_return(false)
152
+ allow(@bot).to receive(:on_blocklist?).with(@tweet).and_return(false)
153
+ allow(@bot).to receive(:skip_me?).with(@tweet).and_return(false)
154
+ allow(@bot).to receive(:interact_with_user?).with(@tweet).and_return(false)
155
+
156
+ expect(@bot.valid_tweet?(@tweet)).to eql(false)
157
+ end
158
+
159
+ it "is true otherwise" do
160
+ allow(@bot).to receive(:skippable_retweet?).with(@tweet).and_return(false)
161
+ allow(@bot).to receive(:on_blocklist?).with(@tweet).and_return(false)
162
+ allow(@bot).to receive(:skip_me?).with(@tweet).and_return(false)
163
+ allow(@bot).to receive(:interact_with_user?).with(@tweet).and_return(true)
164
+
165
+ expect(@bot.valid_tweet?(@tweet)).to eql(true)
166
+ end
167
+ end
168
+ end
169
+
170
+ end
data/spec/bot_spec.rb CHANGED
@@ -1,17 +1,92 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
2
 
3
3
  describe "Chatterbot::Bot" do
4
- after(:each) do
5
- b = Chatterbot::Bot.new(:reset_since_id => false)
4
+ before(:each) do
5
+ @bot = Chatterbot::Bot.new
6
6
  end
7
7
 
8
- describe "reset_bot?" do
9
- it "should call reset_since_id and update_config" do
10
- expect_any_instance_of(Chatterbot::Bot).to receive(:reset_since_id)
11
- expect_any_instance_of(Chatterbot::Bot).to receive(:update_config)
12
- allow_any_instance_of(Chatterbot::Bot).to receive(:exit)
13
- b = Chatterbot::Bot.new(:reset_since_id => true)
8
+ describe "Streaming API" do
9
+ it "should call streaming_client.user" do
10
+ expect(@bot.streaming_client).to receive(:user)
11
+ @bot.stream!
14
12
  end
13
+ end
14
+
15
+ describe "REST API" do
16
+ it "should work" do
17
+ expect(@bot).to receive(:require_login).and_return(true)
18
+ expect(@bot).to receive(:client).and_return(fake_home_timeline(3))
19
+ @bot.register_handler(:home_timeline) {}
20
+ @bot.run!
21
+ end
22
+ end
23
+
24
+ describe "run_or_stream" do
25
+ it "should use streaming if specified" do
26
+ expect(@bot).to receive(:stream!)
27
+ @bot.streaming = true
28
+ @bot.run_or_stream
29
+ end
30
+
31
+ it "should use streaming if required by handler" do
32
+ expect(@bot).to receive(:stream!)
33
+ @bot.register_handler(:deleted) {}
34
+ @bot.run_or_stream
35
+ end
36
+
37
+ it "should use REST if specified" do
38
+ expect(@bot).to receive(:run!)
39
+ @bot.run_or_stream
40
+ end
41
+ end
42
+
43
+
44
+
45
+ describe "stream!" do
46
+ before(:each) do
47
+ @sc = double(Twitter::Client)
48
+ expect(@bot).to receive(:streaming_client).and_return(@sc)
49
+ end
50
+
51
+ it "tweaks settings for searches" do
52
+ @bot.register_handler(:search, "hello") {}
53
+ expect(@sc).to receive(:filter)
15
54
 
55
+ @bot.stream!
56
+ end
57
+
58
+ it "calls :user for non-searches" do
59
+ @bot.register_handler(:home_timeline) {}
60
+ expect(@sc).to receive(:user)
61
+
62
+ @bot.stream!
63
+ end
16
64
  end
65
+
66
+ describe "streamify_search_options" do
67
+ it "works with string" do
68
+ expect( @bot.streamify_search_options("hello there") ).
69
+ to eql({track:"hello there"})
70
+
71
+
72
+ expect( @bot.streamify_search_options("hello there, friend") ).
73
+ to eql({track:"hello there, friend"})
74
+ end
75
+
76
+ it "works with array" do
77
+ expect( @bot.streamify_search_options(["hello there"]) ).
78
+ to eql({track:"hello there"})
79
+
80
+ expect( @bot.streamify_search_options(["hello there", "friend"]) ).
81
+ to eql({track:"hello there, friend"})
82
+ end
83
+
84
+ it "works with hash" do
85
+ opts = {filter:"hello"}
86
+ expect( @bot.streamify_search_options(opts) ).
87
+ to eql(opts)
88
+
89
+ end
90
+ end
91
+
17
92
  end
data/spec/client_spec.rb CHANGED
@@ -11,7 +11,43 @@ describe "Chatterbot::Client" do
11
11
  it "should initialize streaming client" do
12
12
  expect(@bot.streaming_client).to be_a(Twitter::Streaming::Client)
13
13
  end
14
+
15
+ describe "reset!" do
16
+ it "should reset a bunch of stuff" do
17
+ @bot.config[:since_id] = 1234
18
+ @bot.config[:since_id_reply] = 1234
19
+
20
+ @bot.reset!
21
+
22
+ expect(@bot.config[:since_id]).to eql(1)
23
+ expect(@bot.config[:since_id_reply]).to eql(1)
24
+ end
25
+ end
26
+
27
+ describe "reset_since_id_counters" do
28
+ it "should reset a bunch of stuff" do
29
+ expect(@bot).to receive(:reset!)
30
+ expect(@bot).to receive(:reset_since_id)
31
+ expect(@bot).to receive(:reset_since_id_reply)
32
+ expect(@bot).to receive(:reset_since_id_home_timeline)
33
+ expect(@bot).to receive(:reset_since_id_dm)
34
+
35
+ @bot.reset_since_id_counters
36
+ end
37
+ end
14
38
 
39
+ describe "reset_since_id" do
40
+ it "runs a search to get a new max_id" do
41
+ bot = test_bot
42
+
43
+ allow(bot).to receive(:client).and_return(fake_search(100, 1))
44
+ expect(bot.client).to receive(:search).with("a", anything)
45
+ bot.reset_since_id
46
+
47
+ expect(bot.config[:since_id]).to eq(100)
48
+ end
49
+ end
50
+
15
51
  describe "reset_since_id_reply" do
16
52
  it "gets the id of the last reply" do
17
53
  bot = test_bot
@@ -20,21 +56,32 @@ describe "Chatterbot::Client" do
20
56
 
21
57
  bot.reset_since_id_reply
22
58
 
23
- expect(bot.config[:tmp_since_id_reply]).to eq(1000)
59
+ expect(bot.config[:since_id_reply]).to eq(1000)
24
60
  end
25
61
  end
26
62
 
27
- describe "reset_since_id" do
28
- it "runs a search to get a new max_id" do
63
+ describe "reset_since_id_home_timeline" do
64
+ it "gets the id of the last tweet in timeline" do
29
65
  bot = test_bot
66
+ allow(bot).to receive(:client).and_return(fake_home_timeline(3))
30
67
 
31
- allow(bot).to receive(:client).and_return(fake_search(100, 1))
32
- expect(bot.client).to receive(:search).with("a")
33
- bot.reset_since_id
68
+ bot.reset_since_id_home_timeline
34
69
 
35
- expect(bot.config[:tmp_since_id]).to eq(100)
70
+ expect(bot.config[:since_id_home_timeline]).to eq(3)
36
71
  end
37
72
  end
73
+
74
+ describe "reset_since_id_dm" do
75
+ it "gets the id of the last direct message" do
76
+ bot = test_bot
77
+ allow(bot).to receive(:client).and_return(fake_direct_messages(3))
78
+
79
+ bot.reset_since_id_dm
80
+
81
+ expect(bot.config[:since_id_dm]).to eq(3)
82
+ end
83
+ end
84
+
38
85
 
39
86
  it "runs init_client and login on #require_login" do
40
87
  expect(@bot).to receive(:init_client).and_return(true)
@@ -121,6 +168,13 @@ describe "Chatterbot::Client" do
121
168
  end
122
169
  end
123
170
 
171
+ describe "authenticated_user" do
172
+ let(:bot) { test_bot }
173
+ it "should get user from client" do
174
+ expect(bot.client).to receive(:user).and_return('user')
175
+ expect(bot.authenticated_user).to eql('user')
176
+ end
177
+ end
124
178
 
125
179
 
126
180
  end
@@ -0,0 +1,59 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+ require 'tempfile'
3
+
4
+ describe "Chatterbot::ConfigManager" do
5
+ before(:each) do
6
+ @tmp_config_dest = "/tmp/bot.yml"
7
+ @config = Chatterbot::ConfigManager.new(@tmp_config_dest, {:consumer_key => "bar"})
8
+ end
9
+ after(:each) do
10
+ if File.exist?(@tmp_config_dest)
11
+ File.unlink(@tmp_config_dest)
12
+ end
13
+ end
14
+
15
+ describe "delete" do
16
+ it "deletes a key" do
17
+ expect(@config.delete(:baz)).to be_nil
18
+ end
19
+
20
+ it "works with missing key" do
21
+
22
+ end
23
+
24
+ it "retains read-only data" do
25
+ expect(@config[:consumer_key]).to eql("bar")
26
+ expect(@config.delete(:consumer_key)).to be_nil
27
+ expect(@config[:consumer_key]).to eql("bar")
28
+ end
29
+ end
30
+
31
+ describe "[]=" do
32
+ it "writes a key" do
33
+ expect(@config[:baz]).to be_nil
34
+ @config[:baz] = "hello"
35
+ expect(@config[:baz]).to eql("hello")
36
+ end
37
+
38
+ it "does not overwrite read-only data" do
39
+ expect(@config[:consumer_key]).to eql("bar")
40
+ @config[:consumer_key] = "baz"
41
+ expect(@config[:consumer_key]).to eql("bar")
42
+ end
43
+ end
44
+
45
+ describe "[]" do
46
+ it "works with missing key" do
47
+ expect(@config[:baz]).to be_nil
48
+ end
49
+
50
+ it "works with read-only data" do
51
+ expect(@config[:consumer_key]).to eql("bar")
52
+ end
53
+
54
+ it "works with present key" do
55
+ @config[:baz] = "hello"
56
+ expect(@config[:baz]).to eql("hello")
57
+ end
58
+ end
59
+ end