chatterbot 0.5.1 → 0.6.1
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/README.markdown +81 -12
- data/bin/chatterbot-register +3 -3
- data/chatterbot.gemspec +13 -28
- data/examples/echoes_bot.rb +4 -4
- data/lib/chatterbot.rb +5 -2
- data/lib/chatterbot/blacklist.rb +2 -2
- data/lib/chatterbot/bot.rb +13 -6
- data/lib/chatterbot/client.rb +103 -61
- data/lib/chatterbot/config.rb +25 -13
- data/lib/chatterbot/dsl.rb +104 -45
- data/lib/chatterbot/helpers.rb +3 -0
- data/lib/chatterbot/reply.rb +9 -13
- data/lib/chatterbot/retweet.rb +1 -1
- data/lib/chatterbot/search.rb +10 -14
- data/lib/chatterbot/tweet.rb +6 -4
- data/lib/chatterbot/ui.rb +96 -0
- data/lib/chatterbot/version.rb +1 -1
- data/spec/blacklist_spec.rb +4 -4
- data/spec/bot_spec.rb +1 -1
- data/spec/client_spec.rb +40 -10
- data/spec/config_spec.rb +24 -9
- data/spec/dsl_spec.rb +12 -6
- data/spec/helpers_spec.rb +1 -1
- data/spec/reply_spec.rb +17 -23
- data/spec/retweet_spec.rb +2 -2
- data/spec/search_spec.rb +21 -26
- data/spec/spec_helper.rb +25 -17
- data/spec/tweet_spec.rb +4 -4
- metadata +136 -18
data/lib/chatterbot/config.rb
CHANGED
@@ -41,6 +41,13 @@ module Chatterbot
|
|
41
41
|
def no_update=(d)
|
42
42
|
config[:no_update] = d
|
43
43
|
end
|
44
|
+
|
45
|
+
#
|
46
|
+
# should we reset the since_id for this bot?
|
47
|
+
#
|
48
|
+
def reset_bot?
|
49
|
+
config[:reset_since_id] || false
|
50
|
+
end
|
44
51
|
|
45
52
|
#
|
46
53
|
# are we in debug mode?
|
@@ -105,19 +112,24 @@ module Chatterbot
|
|
105
112
|
# update the since_id with either the highest ID of the specified
|
106
113
|
# tweets, unless it is lower than what we have already
|
107
114
|
def update_since_id(search)
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
115
|
+
return if search.nil?
|
116
|
+
|
117
|
+
tmp_id = case
|
118
|
+
# Twitter::SearchResults
|
119
|
+
when search.respond_to?(:max_id) then search.max_id
|
120
|
+
|
121
|
+
# incoming tweets
|
122
|
+
when search.respond_to?(:id) then search.id
|
123
|
+
|
124
|
+
# an enumeration
|
125
|
+
when search.respond_to?(:max) then search.max { |a, b| a.id <=> b.id }.id
|
126
|
+
|
127
|
+
# probably an actual tweet ID at this point,
|
128
|
+
# otherwise it will fail and return 0
|
129
|
+
else search
|
130
|
+
end.to_i
|
131
|
+
|
132
|
+
config[:tmp_since_id] = [config[:tmp_since_id].to_i, tmp_id].max
|
121
133
|
end
|
122
134
|
|
123
135
|
#
|
data/lib/chatterbot/dsl.rb
CHANGED
@@ -6,6 +6,60 @@ module Chatterbot
|
|
6
6
|
# very basic DSL to handle the common stuff you would want to do with a bot.
|
7
7
|
module DSL
|
8
8
|
|
9
|
+
#
|
10
|
+
# search twitter for the specified terms, then pass any matches to
|
11
|
+
# the block.
|
12
|
+
# @example
|
13
|
+
# search("chatterbot is cool!") do |tweet|
|
14
|
+
# puts tweet[:text] # this is the actual tweeted text
|
15
|
+
# reply "I agree!", tweet
|
16
|
+
# end
|
17
|
+
def search(query, opts = {}, &block)
|
18
|
+
bot.search(query, opts, &block)
|
19
|
+
end
|
20
|
+
|
21
|
+
#
|
22
|
+
# handle replies to the bot. Each time this is called, chatterbot
|
23
|
+
# will pass any replies since the last call to the specified block
|
24
|
+
#
|
25
|
+
# @example
|
26
|
+
# replies do |tweet|
|
27
|
+
# puts tweet[:text] # this is the actual tweeted text
|
28
|
+
# reply "Thanks for the mention!", tweet
|
29
|
+
# end
|
30
|
+
def replies(&block)
|
31
|
+
bot.replies(&block)
|
32
|
+
end
|
33
|
+
|
34
|
+
#
|
35
|
+
# send a tweet
|
36
|
+
#
|
37
|
+
# @param [String] txt the text you want to tweet
|
38
|
+
# @param [Hash] params opts for the tweet
|
39
|
+
# @see http://rdoc.info/gems/twitter/Twitter/API#update-instance_method
|
40
|
+
# @param [Tweet] original if this is a reply, the original tweet. this will
|
41
|
+
# be used for variable substitution, and for logging
|
42
|
+
def tweet(txt, params = {}, original = nil)
|
43
|
+
bot.tweet(txt, params, original)
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
# retweet a tweet
|
48
|
+
# @param [id] id the ID of the tweet
|
49
|
+
def retweet(id)
|
50
|
+
bot.retweet(id)
|
51
|
+
end
|
52
|
+
|
53
|
+
#
|
54
|
+
# reply to a tweet
|
55
|
+
#
|
56
|
+
# @param [String] txt the text you want to tweet
|
57
|
+
# @param [Tweet] source the original tweet you are replying to
|
58
|
+
def reply(txt, source)
|
59
|
+
bot.reply(txt, source)
|
60
|
+
end
|
61
|
+
|
62
|
+
|
9
63
|
#
|
10
64
|
# generate a Bot object. if the DSL is being called from a Bot object, just return it
|
11
65
|
# otherwise create a bot and return that
|
@@ -30,6 +84,10 @@ module Chatterbot
|
|
30
84
|
opts.on('-v', '--verbose', "verbose output to stdout") { params[:verbose] = true }
|
31
85
|
opts.on('--dry-run', "Run the bot in test mode, and also don't update the database") { params[:debug_mode] = true ; params[:no_update] = true }
|
32
86
|
opts.on('-s', '--since_id [ARG]', "Check for tweets since tweet id #[ARG]") { |s| params[:since_id] = s.to_i }
|
87
|
+
opts.on('-r', '--reset', "Reset your bot to ignore old tweets") {
|
88
|
+
params[:debug_mode] = true
|
89
|
+
params[:reset_since_id] = true
|
90
|
+
}
|
33
91
|
|
34
92
|
opts.on_tail("-h", "--help", "Show this message") do
|
35
93
|
puts opts
|
@@ -43,6 +101,7 @@ module Chatterbot
|
|
43
101
|
|
44
102
|
#
|
45
103
|
# should we send tweets?
|
104
|
+
# @param [Boolean] d true/false if we should send tweets
|
46
105
|
#
|
47
106
|
def debug_mode(d=nil)
|
48
107
|
d = true if d.nil?
|
@@ -51,6 +110,7 @@ module Chatterbot
|
|
51
110
|
|
52
111
|
#
|
53
112
|
# should we update the db with a new since_id?
|
113
|
+
# @param [Boolean] d true/false if we should update the database
|
54
114
|
#
|
55
115
|
def no_update(d=nil)
|
56
116
|
d = true if d.nil?
|
@@ -59,6 +119,7 @@ module Chatterbot
|
|
59
119
|
|
60
120
|
#
|
61
121
|
# turn on/off verbose output
|
122
|
+
# @param [Boolean] d true/false use verbose output
|
62
123
|
#
|
63
124
|
def verbose(d=nil)
|
64
125
|
d = true if d.nil?
|
@@ -66,24 +127,14 @@ module Chatterbot
|
|
66
127
|
end
|
67
128
|
|
68
129
|
#
|
69
|
-
#
|
70
|
-
#
|
130
|
+
# specify a bot-specific blacklist of users. accepts an array, or a
|
131
|
+
# comma-delimited string. when called, any subsequent calls to
|
132
|
+
# search or replies will filter out these users.
|
71
133
|
#
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
# string, split on commas and turn into array
|
76
|
-
b.split(",").collect { |s| s.strip }
|
77
|
-
else
|
78
|
-
# presumably an array
|
79
|
-
b
|
80
|
-
end
|
81
|
-
end.flatten
|
82
|
-
end
|
83
|
-
|
134
|
+
# @param [Array, String] args list of usernames
|
135
|
+
# @example
|
136
|
+
# blacklist "mean_user, private_user"
|
84
137
|
#
|
85
|
-
# specify a bot-specific blacklist of users. accepts an array, or a
|
86
|
-
# comma-delimited string
|
87
138
|
def blacklist(*args)
|
88
139
|
list = flatten_list_of_strings(args)
|
89
140
|
|
@@ -95,7 +146,14 @@ module Chatterbot
|
|
95
146
|
end
|
96
147
|
|
97
148
|
#
|
98
|
-
# specify list of strings we will check when deciding to respond
|
149
|
+
# specify list of strings we will check when deciding to respond
|
150
|
+
# to a tweet or not. accepts an array or a comma-delimited string.
|
151
|
+
# when called, any subsequent calls to search or replies will
|
152
|
+
# filter out tweets with these strings
|
153
|
+
#
|
154
|
+
# @param [Array, String] args list of usernames
|
155
|
+
# @example
|
156
|
+
# exclude "spam, junk, something"
|
99
157
|
def exclude(*args)
|
100
158
|
e = flatten_list_of_strings(args)
|
101
159
|
if e.nil? || e.empty?
|
@@ -105,47 +163,48 @@ module Chatterbot
|
|
105
163
|
end
|
106
164
|
end
|
107
165
|
|
166
|
+
|
108
167
|
#
|
109
|
-
#
|
110
|
-
def search(query, opts = {}, &block)
|
111
|
-
bot.search(query, opts, &block)
|
112
|
-
end
|
113
|
-
|
114
|
-
#
|
115
|
-
# handle replies to the bot
|
116
|
-
def replies(&block)
|
117
|
-
bot.replies(&block)
|
118
|
-
end
|
119
|
-
|
168
|
+
# The ID of the most recent tweet processed by the bot
|
120
169
|
#
|
121
|
-
# send a tweet
|
122
|
-
def tweet(txt, params = {}, original = nil)
|
123
|
-
bot.tweet(txt, params, original)
|
124
|
-
end
|
125
|
-
|
126
|
-
#
|
127
|
-
# retweet
|
128
|
-
def retweet(id)
|
129
|
-
bot.retweet(id)
|
130
|
-
end
|
131
|
-
|
132
|
-
#
|
133
|
-
# reply to a tweet
|
134
|
-
def reply(txt, source)
|
135
|
-
bot.reply(txt, source)
|
136
|
-
end
|
137
|
-
|
138
170
|
def since_id
|
139
171
|
bot.config[:since_id]
|
140
172
|
end
|
141
173
|
|
174
|
+
#
|
175
|
+
# explicitly save the configuration/state of the bot.
|
176
|
+
#
|
142
177
|
def update_config
|
143
178
|
bot.update_config
|
144
179
|
end
|
145
180
|
|
181
|
+
#
|
182
|
+
# return the bot's current database connection, if available.
|
183
|
+
# handy if you need to manage data with your bot
|
184
|
+
#
|
146
185
|
def db
|
147
186
|
bot.db
|
148
187
|
end
|
188
|
+
|
189
|
+
|
190
|
+
protected
|
191
|
+
|
192
|
+
#
|
193
|
+
# take a variable list of strings and possibly arrays and turn
|
194
|
+
# them into a single flat array of strings
|
195
|
+
#
|
196
|
+
def flatten_list_of_strings(args)
|
197
|
+
args.collect do |b|
|
198
|
+
if b.is_a?(String)
|
199
|
+
# string, split on commas and turn into array
|
200
|
+
b.split(",").collect { |s| s.strip }
|
201
|
+
else
|
202
|
+
# presumably an array
|
203
|
+
b
|
204
|
+
end
|
205
|
+
end.flatten
|
206
|
+
end
|
207
|
+
|
149
208
|
end
|
150
209
|
end
|
151
210
|
|
data/lib/chatterbot/helpers.rb
CHANGED
@@ -24,6 +24,9 @@ module Chatterbot
|
|
24
24
|
# if we're doing a search, or parsing through replies/mentions.
|
25
25
|
def from_user(s)
|
26
26
|
return s if s.is_a?(String)
|
27
|
+
return s.from_user if s.respond_to?(:from_user) #&& ! s.from_user.nil?
|
28
|
+
# return s.user.screen_name if s.respond_to?(:user)
|
29
|
+
|
27
30
|
s.has_key?(:from_user) ? s[:from_user] : s[:user][:screen_name]
|
28
31
|
end
|
29
32
|
|
data/lib/chatterbot/reply.rb
CHANGED
@@ -7,23 +7,19 @@ module Chatterbot
|
|
7
7
|
# handle replies for the bot
|
8
8
|
def replies(&block)
|
9
9
|
return unless require_login
|
10
|
+
|
10
11
|
debug "check for replies since #{since_id}"
|
11
12
|
|
12
13
|
opts = since_id > 0 ? {:since_id => since_id} : {}
|
13
|
-
|
14
|
+
opts[:count] = 200
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
s
|
20
|
-
|
21
|
-
|
22
|
-
yield s
|
23
|
-
end
|
24
|
-
}
|
25
|
-
end
|
16
|
+
results = client.mentions(opts)
|
17
|
+
results.each { |s|
|
18
|
+
unless ! block_given? || on_blacklist?(s) || skip_me?(s)
|
19
|
+
update_since_id(s)
|
20
|
+
yield s
|
21
|
+
end
|
22
|
+
}
|
26
23
|
end
|
27
|
-
|
28
24
|
end
|
29
25
|
end
|
data/lib/chatterbot/retweet.rb
CHANGED
data/lib/chatterbot/search.rb
CHANGED
@@ -13,8 +13,6 @@ module Chatterbot
|
|
13
13
|
|
14
14
|
# internal search code
|
15
15
|
def search(queries, opts = {}, &block)
|
16
|
-
return unless init_client
|
17
|
-
|
18
16
|
debug "check for tweets since #{since_id}"
|
19
17
|
|
20
18
|
if queries.is_a?(String)
|
@@ -26,19 +24,17 @@ module Chatterbot
|
|
26
24
|
#
|
27
25
|
queries.each { |query|
|
28
26
|
debug "search: #{query} #{opts.merge(default_opts)}"
|
29
|
-
|
30
|
-
|
31
|
-
|
27
|
+
result = search_client.search(
|
28
|
+
exclude_retweets(query),
|
29
|
+
opts.merge(default_opts)
|
30
|
+
)
|
31
|
+
|
32
|
+
update_since_id(result.max_id)
|
32
33
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
#if s[:text].downcase.include?(query.downcase)
|
38
|
-
yield s unless ! block_given? || on_blacklist?(s) || skip_me?(s)
|
39
|
-
#end
|
40
|
-
}
|
41
|
-
end
|
34
|
+
result.collection.each { |s|
|
35
|
+
debug s.text
|
36
|
+
yield s unless ! block_given? || on_blacklist?(s) || skip_me?(s)
|
37
|
+
}
|
42
38
|
}
|
43
39
|
end
|
44
40
|
|
data/lib/chatterbot/tweet.rb
CHANGED
@@ -3,7 +3,6 @@ module Chatterbot
|
|
3
3
|
#
|
4
4
|
# routines for sending tweets
|
5
5
|
module Tweet
|
6
|
-
|
7
6
|
# simple wrapper for sending a message
|
8
7
|
def tweet(txt, params = {}, original = nil)
|
9
8
|
return if require_login == false
|
@@ -17,12 +16,15 @@ module Chatterbot
|
|
17
16
|
log txt, original
|
18
17
|
client.update txt, params
|
19
18
|
end
|
20
|
-
|
19
|
+
rescue Twitter::Error::Forbidden => e
|
20
|
+
debug e
|
21
|
+
false
|
22
|
+
end
|
21
23
|
|
22
24
|
# reply to a tweet
|
23
25
|
def reply(txt, source)
|
24
26
|
debug txt
|
25
|
-
|
26
|
-
end
|
27
|
+
tweet txt, {:in_reply_to_status_id => source[:id]}, source
|
28
|
+
end
|
27
29
|
end
|
28
30
|
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module Chatterbot
|
2
|
+
|
3
|
+
#
|
4
|
+
# routines for outputting setup instructions
|
5
|
+
#
|
6
|
+
module UI
|
7
|
+
|
8
|
+
#
|
9
|
+
# print out a message about getting a PIN from twitter, then output
|
10
|
+
# the URL the user needs to visit to authorize
|
11
|
+
#
|
12
|
+
def get_oauth_verifier
|
13
|
+
puts "****************************************"
|
14
|
+
puts "****************************************"
|
15
|
+
puts "**** BOT AUTH TIME! ****"
|
16
|
+
puts "****************************************"
|
17
|
+
puts "****************************************"
|
18
|
+
|
19
|
+
puts "You need to authorize your bot with Twitter.\n\nPlease login to Twitter under the bot's account. When you're ready, hit Enter.\n\nYour browser will open with the following URL, where you can authorize the bot.\n\n"
|
20
|
+
|
21
|
+
url = request_token.authorize_url
|
22
|
+
|
23
|
+
puts url
|
24
|
+
|
25
|
+
puts "\nIf that doesn't work, you can open the URL in your browser manually."
|
26
|
+
|
27
|
+
puts "\n\nHit enter to start.\n\n"
|
28
|
+
|
29
|
+
STDIN.readline.chomp
|
30
|
+
|
31
|
+
Launchy.open(url)
|
32
|
+
|
33
|
+
# sleep here so that if launchy has any output (which it does
|
34
|
+
# sometimes), it doesn't interfere with our input prompt
|
35
|
+
|
36
|
+
sleep(1)
|
37
|
+
|
38
|
+
puts "Paste your PIN and hit enter when you have completed authorization.\n\n"
|
39
|
+
print "> "
|
40
|
+
|
41
|
+
STDIN.readline.chomp
|
42
|
+
rescue Interrupt => e
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
# Ask the user to get an API key from Twitter.
|
48
|
+
def get_api_key
|
49
|
+
puts "****************************************"
|
50
|
+
puts "****************************************"
|
51
|
+
puts "**** API SETUP TIME! ****"
|
52
|
+
puts "****************************************"
|
53
|
+
puts "****************************************"
|
54
|
+
|
55
|
+
api_url = "https://dev.twitter.com/apps/new"
|
56
|
+
|
57
|
+
puts "Hey, looks like you need to get an API key from Twitter before you can get started."
|
58
|
+
puts "Please hit enter, and I will send you to #{api_url} to start the process."
|
59
|
+
puts "(If it doesn't work, you can open a browser and paste the URL in manually)"
|
60
|
+
|
61
|
+
puts "\nHit Enter to continue."
|
62
|
+
|
63
|
+
STDIN.readline
|
64
|
+
|
65
|
+
Launchy.open(api_url)
|
66
|
+
# pause to allow any launchy output
|
67
|
+
sleep(1)
|
68
|
+
|
69
|
+
puts "\n\n"
|
70
|
+
|
71
|
+
print "\n\nPaste the 'Consumer Key' here: "
|
72
|
+
STDOUT.flush
|
73
|
+
config[:consumer_key] = STDIN.readline.chomp
|
74
|
+
|
75
|
+
print "Paste the 'Consumer Secret' here: "
|
76
|
+
STDOUT.flush
|
77
|
+
config[:consumer_secret] = STDIN.readline.chomp
|
78
|
+
|
79
|
+
# reset the client so we can re-init with new OAuth credentials
|
80
|
+
reset_client
|
81
|
+
|
82
|
+
#
|
83
|
+
# capture ctrl-c and exit without a stack trace
|
84
|
+
#
|
85
|
+
rescue Interrupt => e
|
86
|
+
exit
|
87
|
+
end
|
88
|
+
|
89
|
+
#
|
90
|
+
# error message for auth
|
91
|
+
def display_oauth_error
|
92
|
+
STDERR.puts "Oops! Looks like something went wrong there, please try again!"
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
end
|