chatterbot 0.7.1 → 0.9.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.
- checksums.yaml +4 -4
- data/.travis.yml +1 -1
- data/Gemfile +7 -5
- data/README.markdown +1 -1
- data/Rakefile +8 -0
- data/bin/chatterbot-blacklist +2 -0
- data/bin/chatterbot-register +2 -0
- data/bin/chatterbot-status +2 -0
- data/chatterbot.gemspec +6 -6
- data/examples/streaming_bot.rb +42 -0
- data/lib/chatterbot.rb +16 -1
- data/lib/chatterbot/blacklist.rb +3 -1
- data/lib/chatterbot/bot.rb +4 -0
- data/lib/chatterbot/client.rb +21 -26
- data/lib/chatterbot/config.rb +26 -30
- data/lib/chatterbot/db.rb +2 -0
- data/lib/chatterbot/dsl.rb +117 -3
- data/lib/chatterbot/favorite.rb +23 -0
- data/lib/chatterbot/followers.rb +9 -0
- data/lib/chatterbot/helpers.rb +3 -1
- data/lib/chatterbot/logging.rb +4 -3
- data/lib/chatterbot/profile.rb +40 -0
- data/lib/chatterbot/reply.rb +10 -2
- data/lib/chatterbot/retweet.rb +10 -4
- data/lib/chatterbot/search.rb +12 -6
- data/lib/chatterbot/skeleton.rb +6 -0
- data/lib/chatterbot/streaming.rb +67 -0
- data/lib/chatterbot/streaming_handler.rb +96 -0
- data/lib/chatterbot/tweet.rb +2 -0
- data/lib/chatterbot/ui.rb +2 -1
- data/lib/chatterbot/utils.rb +11 -0
- data/lib/chatterbot/version.rb +1 -1
- data/spec/blacklist_spec.rb +24 -23
- data/spec/bot_spec.rb +12 -0
- data/spec/client_spec.rb +36 -32
- data/spec/config_spec.rb +135 -85
- data/spec/db_spec.rb +5 -5
- data/spec/dsl_spec.rb +74 -27
- data/spec/favorite_spec.rb +34 -0
- data/spec/followers_spec.rb +32 -11
- data/spec/helpers_spec.rb +25 -20
- data/spec/logging_spec.rb +16 -15
- data/spec/profile_spec.rb +65 -0
- data/spec/reply_spec.rb +24 -24
- data/spec/retweet_spec.rb +17 -9
- data/spec/search_spec.rb +26 -26
- data/spec/skeleton_spec.rb +6 -6
- data/spec/spec_helper.rb +44 -32
- data/spec/streaming_handler_spec.rb +78 -0
- data/spec/streaming_spec.rb +172 -0
- data/spec/tweet_spec.rb +18 -18
- data/spec/utils_spec.rb +29 -0
- data/specs.watchr +1 -13
- metadata +27 -16
@@ -0,0 +1,23 @@
|
|
1
|
+
module Chatterbot
|
2
|
+
|
3
|
+
# routines for favorites
|
4
|
+
module Favorite
|
5
|
+
|
6
|
+
# simple wrapper for favoriting a message
|
7
|
+
# @param [id] id A tweet or the ID of a tweet. if not specified,
|
8
|
+
# tries to use the current tweet if available
|
9
|
+
def favorite(id=@current_tweet)
|
10
|
+
return if require_login == false
|
11
|
+
|
12
|
+
id = id_from_tweet(id)
|
13
|
+
|
14
|
+
#:nocov:
|
15
|
+
if debug_mode?
|
16
|
+
debug "I'm in debug mode, otherwise I would favorite tweet id: #{id}"
|
17
|
+
return
|
18
|
+
end
|
19
|
+
#:nocov:
|
20
|
+
client.favorite id
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/chatterbot/followers.rb
CHANGED
data/lib/chatterbot/helpers.rb
CHANGED
data/lib/chatterbot/logging.rb
CHANGED
@@ -27,10 +27,11 @@ module Chatterbot
|
|
27
27
|
return unless log_tweets?
|
28
28
|
|
29
29
|
data = {:txt => txt, :bot => botname, :created_at => Time.now}
|
30
|
+
|
30
31
|
if source != nil
|
31
|
-
data = data.merge(:user => source
|
32
|
-
:source_id => source
|
33
|
-
:source_tweet => source
|
32
|
+
data = data.merge(:user => source.user.screen_name,
|
33
|
+
:source_id => source.id,
|
34
|
+
:source_tweet => source.text)
|
34
35
|
end
|
35
36
|
|
36
37
|
# populate the table
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Chatterbot
|
2
|
+
|
3
|
+
# routines for managing your profile
|
4
|
+
module Profile
|
5
|
+
|
6
|
+
#
|
7
|
+
# get/set the profile description
|
8
|
+
#
|
9
|
+
def profile_text(p=nil)
|
10
|
+
return if require_login == false
|
11
|
+
|
12
|
+
if p.nil?
|
13
|
+
client.user.description
|
14
|
+
else
|
15
|
+
data = {
|
16
|
+
description: p
|
17
|
+
}
|
18
|
+
client.update_profile(data)
|
19
|
+
p
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
#
|
24
|
+
# get/set the profile URL
|
25
|
+
#
|
26
|
+
def profile_website(w=nil)
|
27
|
+
return if require_login == false
|
28
|
+
|
29
|
+
if w.nil?
|
30
|
+
client.user.website
|
31
|
+
else
|
32
|
+
data = {
|
33
|
+
url: w
|
34
|
+
}
|
35
|
+
client.update_profile(data)
|
36
|
+
w
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/chatterbot/reply.rb
CHANGED
@@ -10,16 +10,24 @@ module Chatterbot
|
|
10
10
|
|
11
11
|
debug "check for replies since #{since_id_reply}"
|
12
12
|
|
13
|
-
opts =
|
13
|
+
opts = {}
|
14
|
+
if since_id_reply > 0
|
15
|
+
opts[:since_id] = since_id_reply
|
16
|
+
elsif since_id > 0
|
17
|
+
opts[:since_id] = since_id
|
18
|
+
end
|
14
19
|
opts[:count] = 200
|
15
20
|
|
16
|
-
results = client.
|
21
|
+
results = client.mentions_timeline(opts)
|
22
|
+
@current_tweet = nil
|
17
23
|
results.each { |s|
|
18
24
|
update_since_id_reply(s)
|
19
25
|
unless ! block_given? || on_blacklist?(s) || skip_me?(s)
|
26
|
+
@current_tweet = s
|
20
27
|
yield s
|
21
28
|
end
|
22
29
|
}
|
30
|
+
@current_tweet = nil
|
23
31
|
end
|
24
32
|
end
|
25
33
|
end
|
data/lib/chatterbot/retweet.rb
CHANGED
@@ -4,14 +4,20 @@ module Chatterbot
|
|
4
4
|
module Retweet
|
5
5
|
|
6
6
|
# simple wrapper for retweeting a message
|
7
|
-
|
8
|
-
|
7
|
+
# @param [id] id A tweet or the ID of a tweet. if not specified,
|
8
|
+
# tries to use the current tweet if available
|
9
|
+
def retweet(id=@current_tweet)
|
10
|
+
return if require_login == false || id.nil?
|
9
11
|
|
12
|
+
id = id_from_tweet(id)
|
13
|
+
#:nocov:
|
10
14
|
if debug_mode?
|
11
15
|
debug "I'm in debug mode, otherwise I would retweet with tweet id: #{id}"
|
12
|
-
|
13
|
-
client.retweet id
|
16
|
+
return
|
14
17
|
end
|
18
|
+
#:nocov:
|
19
|
+
|
20
|
+
client.retweet id
|
15
21
|
end
|
16
22
|
end
|
17
23
|
end
|
data/lib/chatterbot/search.rb
CHANGED
@@ -18,22 +18,28 @@ module Chatterbot
|
|
18
18
|
if queries.is_a?(String)
|
19
19
|
queries = [queries]
|
20
20
|
end
|
21
|
+
|
21
22
|
|
22
23
|
#
|
23
24
|
# search twitter
|
24
25
|
#
|
25
26
|
queries.each { |query|
|
26
|
-
debug "search: #{query} #{
|
27
|
-
result =
|
27
|
+
debug "search: #{query} #{default_opts.merge(opts)}"
|
28
|
+
result = client.search(
|
28
29
|
exclude_retweets(query),
|
29
|
-
|
30
|
+
default_opts.merge(opts)
|
30
31
|
)
|
31
|
-
update_since_id(result
|
32
|
+
update_since_id(result)
|
32
33
|
|
33
|
-
|
34
|
+
@current_tweet = nil
|
35
|
+
result.each { |s|
|
34
36
|
debug s.text
|
35
|
-
|
37
|
+
if block_given? && !on_blacklist?(s) && !skip_me?(s)
|
38
|
+
@current_tweet = s
|
39
|
+
yield s
|
40
|
+
end
|
36
41
|
}
|
42
|
+
@current_tweet = nil
|
37
43
|
}
|
38
44
|
end
|
39
45
|
|
data/lib/chatterbot/skeleton.rb
CHANGED
@@ -16,7 +16,10 @@ module Chatterbot
|
|
16
16
|
puts opts.inspect
|
17
17
|
|
18
18
|
if RUBY_VERSION =~ /^1\.8\./
|
19
|
+
#:nocov:
|
19
20
|
apply_vars(src, opts)
|
21
|
+
#:nocov:
|
22
|
+
|
20
23
|
else
|
21
24
|
src % opts
|
22
25
|
end
|
@@ -26,6 +29,7 @@ module Chatterbot
|
|
26
29
|
# handle string interpolation in ruby 1.8. modified from
|
27
30
|
# https://raw.github.com/svenfuchs/i18n/master/lib/i18n/core_ext/string/interpolate.rb
|
28
31
|
#
|
32
|
+
#:nocov:
|
29
33
|
def apply_vars(s, args)
|
30
34
|
pattern = Regexp.union(
|
31
35
|
/%\{(\w+)\}/, # matches placeholders like "%{foo}"
|
@@ -47,6 +51,8 @@ module Chatterbot
|
|
47
51
|
end
|
48
52
|
end
|
49
53
|
end
|
54
|
+
#:nocov:
|
55
|
+
|
50
56
|
end
|
51
57
|
end
|
52
58
|
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Chatterbot
|
2
|
+
|
3
|
+
#
|
4
|
+
# simple twitter stream handler
|
5
|
+
module Streaming
|
6
|
+
|
7
|
+
def authenticated_user
|
8
|
+
@user ||= client.user
|
9
|
+
end
|
10
|
+
|
11
|
+
# Streams messages for a single user, optionally including an
|
12
|
+
# additional search/etc
|
13
|
+
#
|
14
|
+
# @param opts [Hash] options
|
15
|
+
# @option options [String] :with Specifies whether to return information for just the users specified in the follow parameter, or include messages from accounts they follow.
|
16
|
+
# @option options [String] :replies Specifies whether to return additional @replies.
|
17
|
+
# @option options [String] :stall_warnings Specifies whether stall warnings should be delivered.
|
18
|
+
# @option options [String] :track Includes additional Tweets matching the specified keywords. Phrases of keywords are specified by a comma-separated list.
|
19
|
+
# @option options [String] :locations Includes additional Tweets falling within the specified bounding boxes.
|
20
|
+
# @yield [Twitter::Tweet, Twitter::Streaming:
|
21
|
+
def do_streaming(streamer)
|
22
|
+
debug "streaming twitter client"
|
23
|
+
streaming_client.send(streamer.endpoint, streamer.connection_params) do |object|
|
24
|
+
handle_streaming_object(object, streamer)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def handle_streaming_object(object, streamer)
|
29
|
+
debug object
|
30
|
+
|
31
|
+
case object
|
32
|
+
when Twitter::Tweet
|
33
|
+
if object.user == authenticated_user
|
34
|
+
puts "skipping #{object} because it's from me"
|
35
|
+
elsif streamer.tweet_handler && !on_blacklist?(object) && !skip_me?(object)
|
36
|
+
@current_tweet = object
|
37
|
+
streamer.tweet_handler.call object
|
38
|
+
@current_tweet = nil
|
39
|
+
end
|
40
|
+
when Twitter::Streaming::DeletedTweet
|
41
|
+
if streamer.delete_handler
|
42
|
+
streamer.delete_handler.call(object)
|
43
|
+
end
|
44
|
+
when Twitter::DirectMessage
|
45
|
+
if streamer.dm_handler # && !on_blacklist?(object) && !skip_me?(object)
|
46
|
+
@current_tweet = object
|
47
|
+
streamer.dm_handler.call object
|
48
|
+
@current_tweet = nil
|
49
|
+
end
|
50
|
+
when Twitter::Streaming::Event
|
51
|
+
if object.respond_to?(:source) && object.source == authenticated_user
|
52
|
+
puts "skipping #{object} because it's from me"
|
53
|
+
elsif object.name == :follow && streamer.follow_handler
|
54
|
+
streamer.follow_handler.call(object.source)
|
55
|
+
elsif object.name == :favorite && streamer.favorite_handler
|
56
|
+
streamer.favorite_handler.call(object.source, object.target_object)
|
57
|
+
end
|
58
|
+
when Twitter::Streaming::FriendList
|
59
|
+
debug "got friend list"
|
60
|
+
if streamer.friends_handler
|
61
|
+
streamer.friends_handler.call(object)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
@@ -0,0 +1,96 @@
|
|
1
|
+
|
2
|
+
class StreamingHandler
|
3
|
+
attr_reader :tweet_handler
|
4
|
+
attr_reader :favorite_handler
|
5
|
+
attr_reader :dm_handler
|
6
|
+
attr_reader :follow_handler
|
7
|
+
attr_reader :delete_handler
|
8
|
+
attr_reader :friends_handler
|
9
|
+
attr_reader :search_filter
|
10
|
+
|
11
|
+
attr_accessor :opts
|
12
|
+
|
13
|
+
def initialize(bot, opts = {})
|
14
|
+
@bot = bot
|
15
|
+
@opts = opts
|
16
|
+
end
|
17
|
+
|
18
|
+
def bot
|
19
|
+
@bot
|
20
|
+
end
|
21
|
+
|
22
|
+
def config
|
23
|
+
bot.config
|
24
|
+
end
|
25
|
+
|
26
|
+
# filter, firehose, sample, user
|
27
|
+
def endpoint
|
28
|
+
opts[:endpoint] || :user
|
29
|
+
end
|
30
|
+
|
31
|
+
def search(query, opts = {}, &block)
|
32
|
+
@search_filter = query
|
33
|
+
@search_opts = opts
|
34
|
+
apply_main_block(&block) if block_given?
|
35
|
+
end
|
36
|
+
|
37
|
+
def user(&block)
|
38
|
+
apply_main_block(&block)
|
39
|
+
end
|
40
|
+
alias_method :replies, :user
|
41
|
+
alias_method :timeline, :user
|
42
|
+
alias_method :sample, :user
|
43
|
+
alias_method :filter, :user
|
44
|
+
|
45
|
+
def favorited(&block)
|
46
|
+
@favorite_handler = block
|
47
|
+
end
|
48
|
+
|
49
|
+
def direct_message(&block)
|
50
|
+
@dm_handler = block
|
51
|
+
end
|
52
|
+
|
53
|
+
def followed(&block)
|
54
|
+
@follow_handler = block
|
55
|
+
end
|
56
|
+
|
57
|
+
def delete(&block)
|
58
|
+
@delete_handler = block
|
59
|
+
end
|
60
|
+
|
61
|
+
def friends(&block)
|
62
|
+
@friends_handler = block
|
63
|
+
end
|
64
|
+
|
65
|
+
def connection_params
|
66
|
+
opts = {
|
67
|
+
#:with => 'followings',
|
68
|
+
#:replies => false,
|
69
|
+
:stall_warnings => false
|
70
|
+
}.merge(@opts)
|
71
|
+
|
72
|
+
opts.delete(:endpoint)
|
73
|
+
|
74
|
+
# convert true/false to strings
|
75
|
+
opts.each { |k, v| opts[k] = v.to_s }
|
76
|
+
|
77
|
+
if @search_filter
|
78
|
+
opts[:track] = @search_filter
|
79
|
+
end
|
80
|
+
|
81
|
+
opts
|
82
|
+
end
|
83
|
+
|
84
|
+
def apply_main_block(&block)
|
85
|
+
if !@tweet_handler.nil?
|
86
|
+
warn "WARNING: when using streaming, you may only have one block of user/replies/timeline/sample/filter"
|
87
|
+
raise RuntimeError, 'Unable to load bot'
|
88
|
+
end
|
89
|
+
@tweet_handler = block
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
def apply(block)
|
94
|
+
instance_exec &block
|
95
|
+
end
|
96
|
+
end
|
data/lib/chatterbot/tweet.rb
CHANGED
data/lib/chatterbot/ui.rb
CHANGED
@@ -9,6 +9,7 @@ module Chatterbot
|
|
9
9
|
# print out a message about getting a PIN from twitter, then output
|
10
10
|
# the URL the user needs to visit to authorize
|
11
11
|
#
|
12
|
+
#:nocov:
|
12
13
|
def get_oauth_verifier
|
13
14
|
puts "****************************************"
|
14
15
|
puts "****************************************"
|
@@ -91,6 +92,6 @@ module Chatterbot
|
|
91
92
|
def display_oauth_error
|
92
93
|
STDERR.puts "Oops! Looks like something went wrong there, please try again!"
|
93
94
|
end
|
94
|
-
|
95
95
|
end
|
96
|
+
#:nocov:
|
96
97
|
end
|
data/lib/chatterbot/version.rb
CHANGED
data/spec/blacklist_spec.rb
CHANGED
@@ -4,56 +4,57 @@ describe "Chatterbot::Blacklist" do
|
|
4
4
|
it "has a list of excluded phrases" do
|
5
5
|
@bot = test_bot
|
6
6
|
@bot.exclude = ["junk", "i hate bots", "foobar", "spam"]
|
7
|
-
@bot.skip_me?("did you know that i hate bots?").
|
7
|
+
expect(@bot.skip_me?("did you know that i hate bots?")).to eq(true)
|
8
8
|
end
|
9
9
|
|
10
10
|
describe "skip_me?" do
|
11
11
|
before(:each) do
|
12
12
|
@bot = test_bot
|
13
|
-
@bot.
|
13
|
+
allow(@bot).to receive(:exclude).and_return(["junk", "i hate bots", "foobar", "spam"])
|
14
14
|
end
|
15
15
|
|
16
16
|
it "blocks tweets with phrases we don't want" do
|
17
|
-
@bot.skip_me?("did you know that i hate bots?").
|
17
|
+
expect(@bot.skip_me?("did you know that i hate bots?")).to eq(true)
|
18
18
|
end
|
19
19
|
|
20
20
|
it "isn't case-specific" do
|
21
|
-
@bot.skip_me?("DID YOU KNOW THAT I HATE BOTS?").
|
21
|
+
expect(@bot.skip_me?("DID YOU KNOW THAT I HATE BOTS?")).to eq(true)
|
22
22
|
end
|
23
23
|
|
24
|
-
|
25
24
|
it "allows users we do want" do
|
26
|
-
@bot.skip_me?("a tweet without any bad content").
|
25
|
+
expect(@bot.skip_me?("a tweet without any bad content")).to eq(false)
|
27
26
|
end
|
28
27
|
|
29
28
|
it "works with result hashes" do
|
30
|
-
@bot.skip_me?(Twitter::Tweet.new(:id => 1, :text => "did you know that i hate bots?")).
|
31
|
-
@bot.skip_me?(Twitter::Tweet.new(:id => 1, :text => "a tweet without any bad content")).
|
29
|
+
expect(@bot.skip_me?(Twitter::Tweet.new(:id => 1, :text => "did you know that i hate bots?"))).to eq(true)
|
30
|
+
expect(@bot.skip_me?(Twitter::Tweet.new(:id => 1, :text => "a tweet without any bad content"))).to eq(false)
|
32
31
|
end
|
33
32
|
end
|
34
33
|
|
35
34
|
describe "on_blacklist?" do
|
36
35
|
before(:each) do
|
37
36
|
@bot = test_bot
|
38
|
-
@bot.
|
37
|
+
allow(@bot).to receive(:blacklist).and_return(["skippy", "blippy", "dippy"])
|
39
38
|
end
|
40
39
|
|
41
40
|
it "blocks users we don't want" do
|
42
|
-
@bot.on_blacklist?("skippy").
|
41
|
+
expect(@bot.on_blacklist?("skippy")).to eq(true)
|
43
42
|
end
|
44
43
|
|
45
44
|
it "allows users we do want" do
|
46
|
-
@bot.on_blacklist?("flippy").
|
45
|
+
expect(@bot.on_blacklist?("flippy")).to eq(false)
|
47
46
|
end
|
48
47
|
|
49
48
|
it "isn't case-specific" do
|
50
|
-
@bot.on_blacklist?("FLIPPY").
|
51
|
-
@bot.on_blacklist?("SKIPPY").
|
49
|
+
expect(@bot.on_blacklist?("FLIPPY")).to eq(false)
|
50
|
+
expect(@bot.on_blacklist?("SKIPPY")).to eq(true)
|
52
51
|
end
|
53
52
|
|
54
53
|
it "works with result hashes" do
|
55
|
-
@bot.on_blacklist?(Twitter::Tweet.new(:id => 1,
|
56
|
-
|
54
|
+
expect(@bot.on_blacklist?(Twitter::Tweet.new(:id => 1,
|
55
|
+
:user => {:id => 1, :screen_name => "skippy"}))).to eq(true)
|
56
|
+
expect(@bot.on_blacklist?(Twitter::Tweet.new(:id => 1,
|
57
|
+
:user => {:id => 1, :screen_name => "flippy"}))).to eq(false)
|
57
58
|
end
|
58
59
|
end
|
59
60
|
|
@@ -63,29 +64,29 @@ describe "Chatterbot::Blacklist" do
|
|
63
64
|
end
|
64
65
|
|
65
66
|
it "doesn't freak out if no db" do
|
66
|
-
@bot.
|
67
|
-
@bot.on_global_blacklist?("foobar").
|
67
|
+
expect(@bot).to receive(:has_db?).and_return(false)
|
68
|
+
expect(@bot.on_global_blacklist?("foobar")).to eq(false)
|
68
69
|
end
|
69
70
|
|
70
71
|
it "collects name from the db if it exists" do
|
71
|
-
@bot.
|
72
|
+
allow(@bot).to receive(:has_db?).and_return(true)
|
72
73
|
blacklist_table = double(Object)
|
73
74
|
double_dataset = double(Object, {:count => 1})
|
74
|
-
blacklist_table.
|
75
|
+
expect(blacklist_table).to receive(:where).
|
75
76
|
with({ :user => "a"}).
|
76
77
|
and_return( double_dataset )
|
77
78
|
|
78
79
|
|
79
80
|
missing_dataset = double(Object, {:count => 0})
|
80
|
-
blacklist_table.
|
81
|
+
expect(blacklist_table).to receive(:where).
|
81
82
|
with({ :user => "b"}).
|
82
83
|
and_return( missing_dataset )
|
83
84
|
|
84
|
-
@bot.
|
85
|
+
allow(@bot).to receive(:db).and_return({
|
85
86
|
:blacklist => blacklist_table
|
86
87
|
})
|
87
|
-
@bot.on_global_blacklist?("a").
|
88
|
-
@bot.on_global_blacklist?("b").
|
88
|
+
expect(@bot.on_global_blacklist?("a")).to eq(true)
|
89
|
+
expect(@bot.on_global_blacklist?("b")).to eq(false)
|
89
90
|
end
|
90
91
|
end
|
91
92
|
|