chatterbot 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|