chatterbot 1.0.2 → 2.0.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +2 -2
- data/LICENSE.txt +18 -9
- data/README.markdown +83 -65
- data/bin/chatterbot-register +0 -1
- data/chatterbot.gemspec +3 -10
- data/examples/class_bot.rb +0 -1
- data/examples/echoes_bot.rb +2 -2
- data/examples/search_bot.rb +1 -1
- data/examples/streaming_bot.rb +21 -15
- data/lib/chatterbot.rb +7 -12
- data/lib/chatterbot/blocklist.rb +61 -0
- data/lib/chatterbot/bot.rb +118 -13
- data/lib/chatterbot/client.rb +52 -20
- data/lib/chatterbot/config.rb +92 -215
- data/lib/chatterbot/config_manager.rb +49 -0
- data/lib/chatterbot/direct_messages.rb +46 -0
- data/lib/chatterbot/dsl.rb +157 -78
- data/lib/chatterbot/followers.rb +4 -0
- data/lib/chatterbot/handler.rb +29 -0
- data/lib/chatterbot/helpers.rb +14 -3
- data/lib/chatterbot/home_timeline.rb +5 -8
- data/lib/chatterbot/logging.rb +0 -17
- data/lib/chatterbot/profile.rb +0 -1
- data/lib/chatterbot/reply.rb +6 -11
- data/lib/chatterbot/retweet.rb +2 -6
- data/lib/chatterbot/safelist.rb +33 -0
- data/lib/chatterbot/search.rb +26 -16
- data/lib/chatterbot/skeleton.rb +7 -38
- data/lib/chatterbot/streaming.rb +26 -33
- data/lib/chatterbot/tweet.rb +0 -1
- data/lib/chatterbot/ui.rb +9 -2
- data/lib/chatterbot/utils.rb +13 -0
- data/lib/chatterbot/version.rb +1 -1
- data/spec/blocklist_spec.rb +170 -0
- data/spec/bot_spec.rb +83 -8
- data/spec/client_spec.rb +61 -7
- data/spec/config_manager_spec.rb +59 -0
- data/spec/config_spec.rb +33 -158
- data/spec/direct_messages_spec.rb +115 -0
- data/spec/dsl_spec.rb +95 -53
- data/spec/handler_spec.rb +27 -0
- data/spec/helpers_spec.rb +7 -11
- data/spec/home_timeline_spec.rb +42 -31
- data/spec/logging_spec.rb +0 -34
- data/spec/reply_spec.rb +10 -34
- data/spec/search_spec.rb +65 -6
- data/spec/spec_helper.rb +25 -1
- data/spec/streaming_spec.rb +56 -58
- data/spec/whitelist_spec.rb +10 -10
- data/specs.watchr +2 -4
- data/templates/skeleton.txt +148 -12
- metadata +20 -22
- data/bin/chatterbot-blacklist +0 -65
- data/bin/chatterbot-status +0 -55
- data/examples/loop_bot.rb +0 -44
- data/lib/chatterbot/blacklist.rb +0 -61
- data/lib/chatterbot/db.rb +0 -79
- data/lib/chatterbot/streaming_handler.rb +0 -96
- data/lib/chatterbot/whitelist.rb +0 -32
- data/spec/blacklist_spec.rb +0 -116
- data/spec/db_spec.rb +0 -53
- data/spec/streaming_handler_spec.rb +0 -78
data/lib/chatterbot/followers.rb
CHANGED
@@ -0,0 +1,29 @@
|
|
1
|
+
module Chatterbot
|
2
|
+
|
3
|
+
#
|
4
|
+
# class for holding onto a block/arguments we will use when calling
|
5
|
+
# methods on the Twitter API
|
6
|
+
#
|
7
|
+
class Handler
|
8
|
+
attr_reader :opts
|
9
|
+
attr_reader :last_ran_at
|
10
|
+
|
11
|
+
def initialize(opts, &block)
|
12
|
+
if block_given?
|
13
|
+
@opts = *opts
|
14
|
+
@block = block
|
15
|
+
else
|
16
|
+
@opts = nil
|
17
|
+
@block = opts
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
#
|
22
|
+
# call the block with the specified arguments
|
23
|
+
#
|
24
|
+
def call(*args)
|
25
|
+
@last_ran_at = Time.now
|
26
|
+
@block.call(*args)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/chatterbot/helpers.rb
CHANGED
@@ -3,6 +3,11 @@ module Chatterbot
|
|
3
3
|
#
|
4
4
|
# a bunch of helper routines for bots
|
5
5
|
module Helpers
|
6
|
+
|
7
|
+
#
|
8
|
+
# Set the username of the bot. This is used when generating
|
9
|
+
# config/skeleton file during registration
|
10
|
+
#
|
6
11
|
def botname=(b)
|
7
12
|
@botname = b
|
8
13
|
end
|
@@ -27,11 +32,9 @@ module Chatterbot
|
|
27
32
|
when Twitter::Tweet
|
28
33
|
s.user.screen_name
|
29
34
|
when Twitter::User
|
30
|
-
s.
|
35
|
+
s.name
|
31
36
|
when String
|
32
37
|
s
|
33
|
-
else
|
34
|
-
s.has_key?(:from_user) ? s[:from_user] : s[:user][:screen_name]
|
35
38
|
end
|
36
39
|
end
|
37
40
|
|
@@ -59,5 +62,13 @@ module Chatterbot
|
|
59
62
|
end
|
60
63
|
end
|
61
64
|
|
65
|
+
#
|
66
|
+
# find the user of the current tweet/object we are dealing with
|
67
|
+
#
|
68
|
+
def current_user
|
69
|
+
return nil if @current_tweet.nil?
|
70
|
+
return @current_tweet.sender if @current_tweet.respond_to?(:sender)
|
71
|
+
return @current_tweet.user
|
72
|
+
end
|
62
73
|
end
|
63
74
|
end
|
@@ -5,24 +5,21 @@ module Chatterbot
|
|
5
5
|
module HomeTimeline
|
6
6
|
|
7
7
|
# handle the bots timeline
|
8
|
-
def home_timeline(
|
8
|
+
def home_timeline(*args, &block)
|
9
9
|
return unless require_login
|
10
10
|
|
11
11
|
debug "check for home_timeline tweets since #{since_id}"
|
12
12
|
|
13
13
|
opts = {
|
14
|
-
:since_id =>
|
14
|
+
:since_id => since_id_home_timeline,
|
15
15
|
:count => 200
|
16
|
-
}
|
17
|
-
|
16
|
+
}
|
18
17
|
results = client.home_timeline(opts)
|
19
18
|
|
20
19
|
@current_tweet = nil
|
21
20
|
results.each { |s|
|
22
|
-
|
23
|
-
if
|
24
|
-
debug "skipping because user not on whitelist"
|
25
|
-
elsif block_given? && !on_blacklist?(s) && !skip_me?(s)
|
21
|
+
update_since_id_home_timeline(s)
|
22
|
+
if block_given? && valid_tweet?(s)
|
26
23
|
@current_tweet = s
|
27
24
|
yield s
|
28
25
|
end
|
data/lib/chatterbot/logging.rb
CHANGED
@@ -21,23 +21,6 @@ module Chatterbot
|
|
21
21
|
debug s
|
22
22
|
end
|
23
23
|
|
24
|
-
#
|
25
|
-
# log a tweet to the database
|
26
|
-
def log(txt, source=nil)
|
27
|
-
return unless log_tweets?
|
28
|
-
|
29
|
-
data = {:txt => txt, :bot => botname, :created_at => Time.now}
|
30
|
-
|
31
|
-
if source != nil
|
32
|
-
data = data.merge(:user => source.user.screen_name,
|
33
|
-
:source_id => source.id,
|
34
|
-
:source_tweet => source.text)
|
35
|
-
end
|
36
|
-
|
37
|
-
# populate the table
|
38
|
-
db[:tweets].insert(data)
|
39
|
-
end
|
40
|
-
|
41
24
|
protected
|
42
25
|
#
|
43
26
|
# initialize a Logger object, writing to log_dest
|
data/lib/chatterbot/profile.rb
CHANGED
data/lib/chatterbot/reply.rb
CHANGED
@@ -5,26 +5,21 @@ module Chatterbot
|
|
5
5
|
module Reply
|
6
6
|
|
7
7
|
# handle replies for the bot
|
8
|
-
def replies(&block)
|
8
|
+
def replies(*args, &block)
|
9
9
|
return unless require_login
|
10
10
|
|
11
11
|
debug "check for replies since #{since_id_reply}"
|
12
12
|
|
13
|
-
opts = {
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
opts[:since_id] = since_id
|
18
|
-
end
|
19
|
-
opts[:count] = 200
|
13
|
+
opts = {
|
14
|
+
:since_id => since_id_reply,
|
15
|
+
:count => 200
|
16
|
+
}
|
20
17
|
|
21
18
|
results = client.mentions_timeline(opts)
|
22
19
|
@current_tweet = nil
|
23
20
|
results.each { |s|
|
24
21
|
update_since_id_reply(s)
|
25
|
-
if
|
26
|
-
debug "skipping because user not on whitelist"
|
27
|
-
elsif block_given? && !on_blacklist?(s) && !skip_me?(s)
|
22
|
+
if block_given? && valid_tweet?(s)
|
28
23
|
@current_tweet = s
|
29
24
|
yield s
|
30
25
|
end
|
data/lib/chatterbot/retweet.rb
CHANGED
@@ -0,0 +1,33 @@
|
|
1
|
+
module Chatterbot
|
2
|
+
|
3
|
+
#
|
4
|
+
# methods for only tweeting to users on a specific list
|
5
|
+
module Safelist
|
6
|
+
attr_accessor :safelist
|
7
|
+
|
8
|
+
alias :whitelist :safelist
|
9
|
+
|
10
|
+
# A list of Twitter users that we can communicate with
|
11
|
+
def safelist
|
12
|
+
@safelist || []
|
13
|
+
end
|
14
|
+
|
15
|
+
def safelist=(b)
|
16
|
+
@safelist = b.flatten.collect { |e|
|
17
|
+
from_user(e).downcase
|
18
|
+
}
|
19
|
+
@safelist
|
20
|
+
end
|
21
|
+
|
22
|
+
def has_safelist?
|
23
|
+
!safelist.empty?
|
24
|
+
end
|
25
|
+
|
26
|
+
#
|
27
|
+
# Is this tweet from a user on our safelist?
|
28
|
+
def on_safelist?(s)
|
29
|
+
search = from_user(s).downcase
|
30
|
+
safelist.any? { |b| search.include?(b.downcase) }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/chatterbot/search.rb
CHANGED
@@ -4,21 +4,30 @@ module Chatterbot
|
|
4
4
|
# handle Twitter searches
|
5
5
|
module Search
|
6
6
|
|
7
|
+
# set a reasonable limit on the maximum number of tweets we will
|
8
|
+
# ever return. otherwise it is possible to exceed Twitter's rate limits
|
7
9
|
MAX_SEARCH_TWEETS = 1000
|
8
10
|
|
9
11
|
@skip_retweets = true
|
10
12
|
|
11
13
|
#
|
12
|
-
#
|
14
|
+
# exclude retweets from searches
|
13
15
|
#
|
14
16
|
def exclude_retweets
|
15
17
|
@skip_retweets = true
|
16
18
|
end
|
17
19
|
|
20
|
+
#
|
21
|
+
# include retweets from searches
|
22
|
+
#
|
18
23
|
def include_retweets
|
19
24
|
@skip_retweets = false
|
20
25
|
end
|
21
26
|
|
27
|
+
|
28
|
+
#
|
29
|
+
# check if this is a retweet that we want to skip
|
30
|
+
#
|
22
31
|
def skippable_retweet?(t)
|
23
32
|
@skip_retweets && t.retweeted_status?
|
24
33
|
end
|
@@ -26,31 +35,32 @@ module Chatterbot
|
|
26
35
|
# internal search code
|
27
36
|
def search(queries, opts = {}, &block)
|
28
37
|
debug "check for tweets since #{since_id}"
|
29
|
-
|
38
|
+
|
30
39
|
max_tweets = opts.delete(:limit) || MAX_SEARCH_TWEETS
|
31
40
|
|
32
41
|
if queries.is_a?(String)
|
33
42
|
queries = [queries]
|
34
43
|
end
|
35
44
|
|
45
|
+
query = queries.join(" OR ")
|
46
|
+
|
36
47
|
#
|
37
48
|
# search twitter
|
38
49
|
#
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
end
|
51
|
-
}
|
52
|
-
@current_tweet = nil
|
50
|
+
|
51
|
+
debug "search: #{query} #{default_opts.merge(opts)}"
|
52
|
+
@current_tweet = nil
|
53
|
+
client.search( query, default_opts.merge(opts) ).take(max_tweets).each { |s|
|
54
|
+
update_since_id(s)
|
55
|
+
debug s.text
|
56
|
+
|
57
|
+
if block_given? && valid_tweet?(s)
|
58
|
+
@current_tweet = s
|
59
|
+
yield s
|
60
|
+
end
|
53
61
|
}
|
62
|
+
@current_tweet = nil
|
63
|
+
|
54
64
|
end
|
55
65
|
|
56
66
|
end
|
data/lib/chatterbot/skeleton.rb
CHANGED
@@ -4,10 +4,15 @@ module Chatterbot
|
|
4
4
|
# bot template generator
|
5
5
|
class Skeleton
|
6
6
|
class << self
|
7
|
+
|
8
|
+
#
|
9
|
+
# generate a template file for the specified bot
|
10
|
+
# @param [Bot] bot object
|
11
|
+
#
|
7
12
|
def generate(bot)
|
8
13
|
path = File.join(Chatterbot.libdir, "..", "templates", "skeleton.txt")
|
9
14
|
src = File.read(path)
|
10
|
-
|
15
|
+
|
11
16
|
opts = bot.config.merge({
|
12
17
|
:name => bot.botname,
|
13
18
|
:timestamp => Time.now
|
@@ -15,44 +20,8 @@ module Chatterbot
|
|
15
20
|
|
16
21
|
puts opts.inspect
|
17
22
|
|
18
|
-
|
19
|
-
#:nocov:
|
20
|
-
apply_vars(src, opts)
|
21
|
-
#:nocov:
|
22
|
-
|
23
|
-
else
|
24
|
-
src % opts
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
#
|
29
|
-
# handle string interpolation in ruby 1.8. modified from
|
30
|
-
# https://raw.github.com/svenfuchs/i18n/master/lib/i18n/core_ext/string/interpolate.rb
|
31
|
-
#
|
32
|
-
#:nocov:
|
33
|
-
def apply_vars(s, args)
|
34
|
-
pattern = Regexp.union(
|
35
|
-
/%\{(\w+)\}/, # matches placeholders like "%{foo}"
|
36
|
-
/%<(\w+)>(.*?\d*\.?\d*[bBdiouxXeEfgGcps])/ # matches placeholders like "%<foo>.d"
|
37
|
-
)
|
38
|
-
|
39
|
-
pattern_with_escape = Regexp.union(
|
40
|
-
/%%/,
|
41
|
-
pattern
|
42
|
-
)
|
43
|
-
|
44
|
-
s.gsub(pattern_with_escape) do |match|
|
45
|
-
if match == '%%'
|
46
|
-
'%'
|
47
|
-
else
|
48
|
-
key = ($1 || $2).to_sym
|
49
|
-
raise KeyError unless args.has_key?(key)
|
50
|
-
$3 ? sprintf("%#{$3}", args[key]) : args[key]
|
51
|
-
end
|
52
|
-
end
|
23
|
+
src % opts
|
53
24
|
end
|
54
|
-
#:nocov:
|
55
|
-
|
56
25
|
end
|
57
26
|
end
|
58
27
|
end
|
data/lib/chatterbot/streaming.rb
CHANGED
@@ -4,64 +4,57 @@ module Chatterbot
|
|
4
4
|
# simple twitter stream handler
|
5
5
|
module Streaming
|
6
6
|
|
7
|
-
def
|
8
|
-
|
7
|
+
def streaming_tweet_handler
|
8
|
+
usable_handlers = [:home_timeline, :search]
|
9
|
+
name, block = @handlers.find { |k, v| usable_handlers.include?(k) }
|
10
|
+
block
|
9
11
|
end
|
10
12
|
|
11
|
-
# Streams messages for a single user, optionally including an
|
12
|
-
# additional search/etc
|
13
13
|
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
# @
|
17
|
-
#
|
18
|
-
|
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)
|
14
|
+
# Take the passed in object and call the appropriate bot method
|
15
|
+
# for it
|
16
|
+
# @param [Class] object a streaming API object
|
17
|
+
#
|
18
|
+
def handle_streaming_object(object)
|
29
19
|
debug object
|
30
20
|
|
31
21
|
case object
|
32
22
|
when Twitter::Tweet
|
33
23
|
if object.user == authenticated_user
|
34
24
|
debug "skipping #{object} because it's from me"
|
35
|
-
elsif
|
25
|
+
elsif (h = streaming_tweet_handler) && valid_tweet?(object)
|
36
26
|
@current_tweet = object
|
37
|
-
|
27
|
+
update_since_id(object)
|
28
|
+
|
29
|
+
h.call(object)
|
38
30
|
@current_tweet = nil
|
39
31
|
end
|
40
32
|
when Twitter::Streaming::DeletedTweet
|
41
|
-
if
|
42
|
-
|
33
|
+
if (h = @handlers[:deleted])
|
34
|
+
h.call(object)
|
43
35
|
end
|
44
36
|
when Twitter::DirectMessage
|
45
|
-
if
|
37
|
+
if object.sender == authenticated_user
|
38
|
+
debug "skipping DM #{object} because it's from me"
|
39
|
+
elsif (h = @handlers[:direct_messages])
|
46
40
|
@current_tweet = object
|
47
|
-
|
41
|
+
update_since_id_dm(object)
|
42
|
+
h.call(object)
|
48
43
|
@current_tweet = nil
|
49
44
|
end
|
50
45
|
when Twitter::Streaming::Event
|
51
46
|
if object.respond_to?(:source) && object.source == authenticated_user
|
52
47
|
debug "skipping #{object} because it's from me"
|
53
|
-
elsif object.name == :follow &&
|
54
|
-
|
55
|
-
elsif object.name == :favorite &&
|
56
|
-
|
48
|
+
elsif object.name == :follow && (h = @handlers[:followed])
|
49
|
+
h.call(object.source)
|
50
|
+
elsif object.name == :favorite && (h = @handlers[:favorited])
|
51
|
+
h.call(object.source, object.target_object)
|
57
52
|
end
|
58
53
|
when Twitter::Streaming::FriendList
|
59
54
|
debug "got friend list"
|
60
|
-
if streamer.friends_handler
|
61
|
-
streamer.friends_handler.call(object)
|
62
|
-
end
|
63
55
|
end
|
64
56
|
end
|
65
|
-
|
57
|
+
|
58
|
+
end
|
66
59
|
end
|
67
60
|
|