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.
- 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
|
|