chatterbot 0.2.0
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.
- data/.document +5 -0
- data/.gitignore +52 -0
- data/Gemfile +25 -0
- data/LICENSE.txt +17 -0
- data/README.rdoc +184 -0
- data/Rakefile +26 -0
- data/bin/chatterbot-blacklist +63 -0
- data/chatterbot.gemspec +53 -0
- data/examples/class_bot.rb +9 -0
- data/examples/config.yml.example +6 -0
- data/examples/dsl_test.rb +9 -0
- data/examples/echoes_bot.rb +27 -0
- data/lib/chatterbot.rb +50 -0
- data/lib/chatterbot/blacklist.rb +69 -0
- data/lib/chatterbot/bot.rb +29 -0
- data/lib/chatterbot/client.rb +123 -0
- data/lib/chatterbot/config.rb +259 -0
- data/lib/chatterbot/db.rb +70 -0
- data/lib/chatterbot/dsl.rb +100 -0
- data/lib/chatterbot/helpers.rb +29 -0
- data/lib/chatterbot/logging.rb +48 -0
- data/lib/chatterbot/reply.rb +28 -0
- data/lib/chatterbot/search.rb +35 -0
- data/lib/chatterbot/tweet.rb +25 -0
- data/lib/chatterbot/version.rb +3 -0
- data/spec/blacklist_spec.rb +115 -0
- data/spec/bot_spec.rb +5 -0
- data/spec/client_spec.rb +60 -0
- data/spec/config_spec.rb +204 -0
- data/spec/db_spec.rb +33 -0
- data/spec/dsl_spec.rb +73 -0
- data/spec/helpers_spec.rb +40 -0
- data/spec/logging_spec.rb +65 -0
- data/spec/reply_spec.rb +54 -0
- data/spec/search_spec.rb +72 -0
- data/spec/spec_helper.rb +52 -0
- data/spec/tweet_spec.rb +76 -0
- data/specs.watchr +60 -0
- metadata +162 -0
data/spec/bot_spec.rb
ADDED
data/spec/client_spec.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe "Chatterbot::Client" do
|
4
|
+
before(:each) do
|
5
|
+
@bot = Chatterbot::Bot.new
|
6
|
+
@bot.client = mock(Object)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "runs init_client and login on #require_login" do
|
10
|
+
@bot.should_receive(:init_client).and_return(true)
|
11
|
+
@bot.should_receive(:login).and_return(true)
|
12
|
+
@bot.require_login
|
13
|
+
end
|
14
|
+
|
15
|
+
it "reset_client resets the client instance" do
|
16
|
+
@bot.should_receive(:init_client).and_return(true)
|
17
|
+
@bot.reset_client
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "api setup" do
|
21
|
+
it "calls get_api_key" do
|
22
|
+
@bot.should_receive(:needs_api_key?).and_return(true)
|
23
|
+
@bot.should_receive(:needs_auth_token?).and_return(false)
|
24
|
+
@bot.should_receive(:get_api_key)
|
25
|
+
@bot.login
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "oauth validation" do
|
30
|
+
before(:each) do
|
31
|
+
@bot.should_receive(:needs_api_key?).and_return(false)
|
32
|
+
@bot.should_receive(:needs_auth_token?).and_return(true)
|
33
|
+
@bot.should_receive(:get_oauth_verifier).and_return("pin")
|
34
|
+
@bot.client.should_receive(:request_token).and_return(
|
35
|
+
mock(:token => "token",
|
36
|
+
:secret => "secret"
|
37
|
+
)
|
38
|
+
)
|
39
|
+
@bot.client.should_receive(:authorize).
|
40
|
+
with("token", "secret", { :oauth_verifier => "pin"}).
|
41
|
+
and_return(mock(:token => "access_token", :secret => "access_secret"))
|
42
|
+
end
|
43
|
+
|
44
|
+
it "handles getting an auth token" do
|
45
|
+
@bot.client.should_receive(:authorized?).and_return(true)
|
46
|
+
@bot.should_receive(:update_config)
|
47
|
+
|
48
|
+
@bot.login
|
49
|
+
|
50
|
+
@bot.config[:token].should == "access_token"
|
51
|
+
@bot.config[:secret].should == "access_secret"
|
52
|
+
end
|
53
|
+
|
54
|
+
it "handles errors" do
|
55
|
+
@bot.client.should_receive(:authorized?).and_return(false)
|
56
|
+
@bot.should_receive(:display_oauth_error)
|
57
|
+
@bot.login
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/spec/config_spec.rb
ADDED
@@ -0,0 +1,204 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
require 'tempfile'
|
3
|
+
|
4
|
+
describe "Chatterbot::Config" do
|
5
|
+
before(:each) do
|
6
|
+
@bot = Chatterbot::Bot.new
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "loading" do
|
10
|
+
it "loads config when we need a variable" do
|
11
|
+
@bot.should_receive(:load_config).and_return({:foo => :bar})
|
12
|
+
@bot.config = nil
|
13
|
+
|
14
|
+
@bot.config[:foo].should == :bar
|
15
|
+
end
|
16
|
+
|
17
|
+
it "loads both global config and local config" do
|
18
|
+
@bot.should_receive(:global_config).and_return({:foo => :bar, :a => :b})
|
19
|
+
@bot.should_receive(:bot_config).and_return({:foo => :baz, :custom => :value})
|
20
|
+
|
21
|
+
@bot.config = nil
|
22
|
+
|
23
|
+
|
24
|
+
@bot.config[:foo].should == :baz
|
25
|
+
@bot.config[:a].should == :b
|
26
|
+
@bot.config[:custom].should == :value
|
27
|
+
end
|
28
|
+
|
29
|
+
it "returns a log dest" do
|
30
|
+
@bot.should_receive(:load_config).and_return({:log_dest => :bar})
|
31
|
+
@bot.config = nil
|
32
|
+
|
33
|
+
@bot.log_dest.should == :bar
|
34
|
+
end
|
35
|
+
|
36
|
+
it "checks for an auth_token" do
|
37
|
+
@bot.should_receive(:load_config).and_return({:token => "123"})
|
38
|
+
@bot.config = nil
|
39
|
+
|
40
|
+
@bot.needs_auth_token?.should == false
|
41
|
+
end
|
42
|
+
|
43
|
+
it "checks for an auth_token" do
|
44
|
+
@bot.should_receive(:load_config).and_return({})
|
45
|
+
@bot.config = nil
|
46
|
+
|
47
|
+
@bot.needs_auth_token?.should == true
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
it "checks for an API key" do
|
52
|
+
@bot.should_receive(:load_config).and_return({})
|
53
|
+
@bot.config = nil
|
54
|
+
|
55
|
+
@bot.needs_api_key?.should == true
|
56
|
+
|
57
|
+
@bot.config = {:consumer_key => "ck", :consumer_secret => "cs" }
|
58
|
+
@bot.needs_api_key?.should == false
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "debug_mode?" do
|
63
|
+
it "works when debug_mode isn't set" do
|
64
|
+
@bot.debug_mode?.should == false
|
65
|
+
end
|
66
|
+
|
67
|
+
it "works when debug_mode is set" do
|
68
|
+
@bot.config[:debug_mode] = false
|
69
|
+
@bot.debug_mode?.should == false
|
70
|
+
|
71
|
+
@bot.config[:debug_mode] = true
|
72
|
+
@bot.debug_mode?.should == true
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe "since_id=" do
|
77
|
+
it "works" do
|
78
|
+
@bot.since_id = 123
|
79
|
+
@bot.config[:tmp_since_id].should == 123
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe "update_since_id" do
|
84
|
+
it "works with searches" do
|
85
|
+
@bot.config[:tmp_since_id] = 100
|
86
|
+
@bot.update_since_id({ "max_id" => 1000 })
|
87
|
+
@bot.config[:tmp_since_id].should == 1000
|
88
|
+
end
|
89
|
+
|
90
|
+
it "works with tweets" do
|
91
|
+
@bot.config[:tmp_since_id] = 100
|
92
|
+
@bot.update_since_id({ :id => 1000 })
|
93
|
+
@bot.config[:tmp_since_id].should == 1000
|
94
|
+
end
|
95
|
+
|
96
|
+
it "handles weird results" do
|
97
|
+
@bot.config[:tmp_since_id] = 100
|
98
|
+
@bot.update_since_id({ :foo => 1000 })
|
99
|
+
@bot.config[:tmp_since_id].should == 100
|
100
|
+
end
|
101
|
+
|
102
|
+
it "never rolls back" do
|
103
|
+
@bot.config[:tmp_since_id] = 100
|
104
|
+
@bot.update_since_id({ :id => 50 })
|
105
|
+
@bot.config[:tmp_since_id].should == 100
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
|
110
|
+
describe "update_config" do
|
111
|
+
it "doesn't update the config if update_config? is false" do
|
112
|
+
@bot.should_receive(:update_config?).and_return(false)
|
113
|
+
@bot.should_not_receive(:has_db?)
|
114
|
+
@bot.update_config
|
115
|
+
end
|
116
|
+
|
117
|
+
it "doesn't update keys from the global config" do
|
118
|
+
@bot.stub!(:global_config).and_return({:foo => :bar, :a => :b})
|
119
|
+
@bot.stub!(:bot_config).and_return({:foo => :bar, :custom => :value})
|
120
|
+
|
121
|
+
@bot.config = nil
|
122
|
+
|
123
|
+
@bot.config_to_save.should == { :custom => :value }
|
124
|
+
end
|
125
|
+
|
126
|
+
it "does update keys from the global config if they've been customized" do
|
127
|
+
@bot.stub!(:global_config).and_return({:foo => :bar, :a => :b})
|
128
|
+
@bot.stub!(:bot_config).and_return({:foo => :baz, :custom => :value})
|
129
|
+
|
130
|
+
@bot.config = nil
|
131
|
+
|
132
|
+
@bot.config_to_save.should == { :foo => :baz, :custom => :value }
|
133
|
+
end
|
134
|
+
|
135
|
+
it "updates since_id" do
|
136
|
+
@bot.config[:tmp_since_id] = 100
|
137
|
+
@bot.config_to_save[:since_id].should == 100
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
|
142
|
+
describe "global config files" do
|
143
|
+
it "has an array of global_config_files" do
|
144
|
+
ENV["chatterbot_config"] = "/tmp/foo.yml"
|
145
|
+
@bot.global_config_files.first.should == "/etc/chatterbot.yml"
|
146
|
+
@bot.global_config_files[1].should == "/tmp/foo.yml"
|
147
|
+
@bot.global_config_files.last.should include("/global.yml")
|
148
|
+
end
|
149
|
+
|
150
|
+
it "slurps in all the global_config_files" do
|
151
|
+
@bot.stub(:global_config_files).and_return(["config.yml", "config2.yml", "config3.yml"])
|
152
|
+
@bot.should_receive(:slurp_file).with("config.yml").and_return({:a => 1 })
|
153
|
+
@bot.should_receive(:slurp_file).with("config2.yml").and_return({:b => 2 })
|
154
|
+
@bot.should_receive(:slurp_file).with("config3.yml").and_return({:c => 3 })
|
155
|
+
|
156
|
+
@bot.global_config.should == { :a => 1, :b => 2, :c => 3}
|
157
|
+
end
|
158
|
+
|
159
|
+
it "priorities last file in list" do
|
160
|
+
@bot.stub(:global_config_files).and_return(["config.yml", "config2.yml", "config3.yml"])
|
161
|
+
@bot.should_receive(:slurp_file).with("config.yml").and_return({:a => 1 })
|
162
|
+
@bot.should_receive(:slurp_file).with("config2.yml").and_return({:b => 2 })
|
163
|
+
@bot.should_receive(:slurp_file).with("config3.yml").and_return({:a => 100, :b => 50, :c => 3 })
|
164
|
+
@bot.global_config.should == { :a => 100, :b => 50, :c => 3}
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
|
169
|
+
describe "file I/O" do
|
170
|
+
it "loads in some YAML" do
|
171
|
+
tmp = {:since_id => 0}
|
172
|
+
|
173
|
+
src = Tempfile.new("config")
|
174
|
+
src << tmp.to_yaml
|
175
|
+
src.flush
|
176
|
+
|
177
|
+
@bot.slurp_file(src.path).should == tmp
|
178
|
+
end
|
179
|
+
|
180
|
+
it "symbolizes keys" do
|
181
|
+
tmp = {'since_id' => 0}
|
182
|
+
|
183
|
+
src = Tempfile.new("config")
|
184
|
+
src << tmp.to_yaml
|
185
|
+
src.flush
|
186
|
+
|
187
|
+
@bot.slurp_file(src.path).should == { :since_id => 0 }
|
188
|
+
end
|
189
|
+
|
190
|
+
it "doesn't store local file if we can store to db instead" do
|
191
|
+
@bot.should_receive(:has_db?).and_return(true)
|
192
|
+
@bot.should_receive(:store_database_config)
|
193
|
+
@bot.update_config
|
194
|
+
end
|
195
|
+
|
196
|
+
it "stores local file if no db" do
|
197
|
+
@bot.should_receive(:has_db?).and_return(false)
|
198
|
+
@bot.should_not_receive(:store_database_config)
|
199
|
+
@bot.should_receive(:store_local_config)
|
200
|
+
@bot.update_config
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
end
|
data/spec/db_spec.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
require 'sequel'
|
4
|
+
|
5
|
+
describe "Chatterbot::DB" do
|
6
|
+
before(:each) do
|
7
|
+
@db_tmp = Tempfile.new("config.db")
|
8
|
+
@db_uri = "sqlite://#{@db_tmp.path}"
|
9
|
+
|
10
|
+
@bot = Chatterbot::Bot.new
|
11
|
+
@bot.config[:db_uri] = @db_uri
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "table creation" do
|
15
|
+
[:blacklist, :tweets, :config].each do |table|
|
16
|
+
it "should create table #{table}" do
|
17
|
+
@bot.db
|
18
|
+
@tmp_conn = Sequel.connect(@db_uri)
|
19
|
+
@tmp_conn.tables.include?(table).should == true
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "store_database_config" do
|
25
|
+
it "doesn't fail" do
|
26
|
+
@bot = Chatterbot::Bot.new
|
27
|
+
@bot.config[:db_uri] = @db_uri
|
28
|
+
|
29
|
+
@bot.db
|
30
|
+
@bot.store_database_config.should == true
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/spec/dsl_spec.rb
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe "Chatterbot::DSL" do
|
4
|
+
describe "client routines" do
|
5
|
+
before(:each) do
|
6
|
+
@bot = mock(Chatterbot::Bot)
|
7
|
+
@bot.send :require, 'chatterbot/dsl'
|
8
|
+
|
9
|
+
Chatterbot::DSL.stub!(:bot).and_return(@bot)
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "blacklist" do
|
13
|
+
it "#blacklist passes along to bot object" do
|
14
|
+
@bot.should_receive(:blacklist=).with(["foo"])
|
15
|
+
blacklist ["foo"]
|
16
|
+
end
|
17
|
+
|
18
|
+
it "#blacklist turns single-string arg into an array" do
|
19
|
+
@bot.should_receive(:blacklist=).with(["foo"])
|
20
|
+
blacklist "foo"
|
21
|
+
end
|
22
|
+
|
23
|
+
it "#blacklist turns comma-delimited string arg into an array" do
|
24
|
+
@bot.should_receive(:blacklist=).with(["foo", "bar"])
|
25
|
+
blacklist "foo, bar"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "exclude" do
|
30
|
+
it "#exclude passes along to bot object" do
|
31
|
+
@bot.should_receive(:exclude=).with(["foo"])
|
32
|
+
exclude ["foo"]
|
33
|
+
end
|
34
|
+
|
35
|
+
it "#exclude turns single-string arg into an array" do
|
36
|
+
@bot.should_receive(:exclude=).with(["foo"])
|
37
|
+
exclude "foo"
|
38
|
+
end
|
39
|
+
|
40
|
+
it "#exclude turns comma-delimited string arg into an array" do
|
41
|
+
@bot.should_receive(:exclude=).with(["foo", "bar"])
|
42
|
+
exclude "foo, bar"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
it "#search passes along to bot object" do
|
48
|
+
@bot.should_receive(:search).with("foo")
|
49
|
+
search("foo")
|
50
|
+
end
|
51
|
+
|
52
|
+
it "#search passes along to bot object" do
|
53
|
+
@bot.should_receive(:search).with(["foo","bar"])
|
54
|
+
search(["foo","bar"])
|
55
|
+
end
|
56
|
+
|
57
|
+
it "#replies passes along to bot object" do
|
58
|
+
@bot.should_receive(:replies)
|
59
|
+
replies
|
60
|
+
end
|
61
|
+
|
62
|
+
it "#tweet passes along to bot object" do
|
63
|
+
@bot.should_receive(:tweet).with("hello sailor!", {:foo => "bar" }, nil)
|
64
|
+
tweet "hello sailor!", {:foo => "bar"}
|
65
|
+
end
|
66
|
+
|
67
|
+
it "#reply passes along to bot object" do
|
68
|
+
@bot.should_receive(:reply).with("hello sailor!", { :source => "source "})
|
69
|
+
reply "hello sailor!", { :source => "source "}
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe "Chatterbot::Helpers" do
|
4
|
+
it "#tweet_user works with [:from_user]" do
|
5
|
+
bot = Chatterbot::Bot.new
|
6
|
+
bot.tweet_user({:from_user => "foo"}).should == "@foo"
|
7
|
+
bot.tweet_user({:from_user => "@foo"}).should == "@foo"
|
8
|
+
end
|
9
|
+
|
10
|
+
it "#tweet_user works with [:user][:screen_name]" do
|
11
|
+
bot = Chatterbot::Bot.new
|
12
|
+
bot.tweet_user({:user => {:screen_name => "foo"}}).should == "@foo"
|
13
|
+
bot.tweet_user({:user => {:screen_name => "@foo"}}).should == "@foo"
|
14
|
+
end
|
15
|
+
|
16
|
+
it "#tweet_user works with string" do
|
17
|
+
bot = Chatterbot::Bot.new
|
18
|
+
bot.tweet_user("foo").should == "@foo"
|
19
|
+
bot.tweet_user("@foo").should == "@foo"
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "#botname" do
|
23
|
+
before(:each) do
|
24
|
+
@bot = Chatterbot::Bot.new
|
25
|
+
@bot.client = mock(Object)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "calls botname smartly for inherited classes" do
|
29
|
+
class TestBot < Chatterbot::Bot; end
|
30
|
+
@bot2 = TestBot.new
|
31
|
+
@bot2.botname.should == "testbot"
|
32
|
+
end
|
33
|
+
|
34
|
+
it "calls botname for non-inherited bots" do
|
35
|
+
File.should_receive(:basename).and_return("bot")
|
36
|
+
@bot.botname.should == "bot"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe "Chatterbot::Logging" do
|
4
|
+
describe "debug logging" do
|
5
|
+
before(:each) do
|
6
|
+
@bot = Chatterbot::Bot.new
|
7
|
+
@logger = mock(Logger)
|
8
|
+
@bot.stub!(:logger).and_return(@logger)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should call logger on debug" do
|
12
|
+
@bot.should_receive(:logging?).and_return(true)
|
13
|
+
@logger.should_receive(:debug).with("hi!")
|
14
|
+
@bot.debug "hi!"
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should not call logger when not logging desired" do
|
18
|
+
@bot.should_receive(:logging?).and_return(false)
|
19
|
+
@logger.should_not_receive(:debug)
|
20
|
+
@bot.debug "hi!"
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should call logger and also print output when critical" do
|
24
|
+
@bot.should_receive(:logging?).and_return(true)
|
25
|
+
@logger.should_receive(:debug).with("hi!")
|
26
|
+
@bot.should_receive(:puts).with("hi!")
|
27
|
+
@bot.critical "hi!"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "#log to database" do
|
32
|
+
before(:each) do
|
33
|
+
@db_tmp = Tempfile.new("config.db")
|
34
|
+
@db_uri = "sqlite://#{@db_tmp.path}"
|
35
|
+
|
36
|
+
@bot = Chatterbot::Bot.new
|
37
|
+
@bot.config[:db_uri] = @db_uri
|
38
|
+
|
39
|
+
@bot.should_receive(:log_tweets?).and_return(true)
|
40
|
+
|
41
|
+
@bot.should_receive(:botname).and_return("logger")
|
42
|
+
Time.stub!(:now).and_return(123)
|
43
|
+
|
44
|
+
@tweets_table = mock(Object)
|
45
|
+
@bot.stub!(:db).and_return({
|
46
|
+
:tweets => @tweets_table
|
47
|
+
})
|
48
|
+
end
|
49
|
+
|
50
|
+
it "logs tweets to the db" do
|
51
|
+
@tweets_table.should_receive(:insert).with({ :txt => "TEST", :bot => "logger", :created_at => 123})
|
52
|
+
@bot.log "TEST"
|
53
|
+
end
|
54
|
+
|
55
|
+
it "logs tweets with some source info to the db" do
|
56
|
+
source_tweet = {:from_user => "replytome", :id => 12345, :text => "i should trigger a bot" }
|
57
|
+
|
58
|
+
params = {:txt=>"TEST", :bot=>"logger", :created_at=>123, :user=>"replytome", :source_id=>12345, :source_tweet=>"i should trigger a bot"}
|
59
|
+
|
60
|
+
@tweets_table.should_receive(:insert).with(params)
|
61
|
+
|
62
|
+
@bot.log "TEST", source_tweet
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|