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
@@ -0,0 +1,61 @@
|
|
1
|
+
module Chatterbot
|
2
|
+
|
3
|
+
#
|
4
|
+
# methods for preventing the bot from spamming people who don't want to hear from it
|
5
|
+
module Blocklist
|
6
|
+
attr_accessor :exclude, :blocklist
|
7
|
+
|
8
|
+
alias :blacklist :blocklist
|
9
|
+
|
10
|
+
# return a list of text strings which we will check for in incoming tweets.
|
11
|
+
# If the text is listed, we won't use this tweet
|
12
|
+
def exclude
|
13
|
+
@exclude || []
|
14
|
+
end
|
15
|
+
|
16
|
+
# A list of Twitter users that don't want to hear from the bot.
|
17
|
+
def blocklist
|
18
|
+
@blocklist || []
|
19
|
+
end
|
20
|
+
def blocklist=(b)
|
21
|
+
@blocklist = b
|
22
|
+
end
|
23
|
+
|
24
|
+
#
|
25
|
+
# Based on the text of this tweet, should it be skipped?
|
26
|
+
def skip_me?(s)
|
27
|
+
search = s.respond_to?(:text) ? s.text : s
|
28
|
+
exclude.detect { |e| search.downcase.include?(e) } != nil
|
29
|
+
end
|
30
|
+
|
31
|
+
def interact_with_user?(t)
|
32
|
+
return true unless only_interact_with_followers?
|
33
|
+
u = t.respond_to?(:user) ? t.user : t
|
34
|
+
client.friendship?(u, authenticated_user)
|
35
|
+
end
|
36
|
+
|
37
|
+
def valid_tweet?(object)
|
38
|
+
if has_safelist? && ! on_safelist?(object)
|
39
|
+
debug "skipping because user not on safelist"
|
40
|
+
return false
|
41
|
+
end
|
42
|
+
|
43
|
+
!skippable_retweet?(object) && ! on_blocklist?(object) && ! skip_me?(object) && interact_with_user?(object)
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
# Is this tweet from a user on our blocklist?
|
48
|
+
def on_blocklist?(s)
|
49
|
+
search = if s.is_a?(Twitter::User)
|
50
|
+
s.name
|
51
|
+
elsif s.respond_to?(:user) && !s.is_a?(Twitter::NullObject)
|
52
|
+
from_user(s)
|
53
|
+
else
|
54
|
+
s
|
55
|
+
end.downcase
|
56
|
+
|
57
|
+
blocklist.any? { |b| search.include?(b.downcase) }
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
data/lib/chatterbot/bot.rb
CHANGED
@@ -1,15 +1,17 @@
|
|
1
1
|
module Chatterbot
|
2
|
+
require 'chatterbot/handler'
|
2
3
|
|
3
4
|
#
|
4
5
|
# primary Bot object, includes all the other modules
|
5
6
|
class Bot
|
6
7
|
include Utils
|
7
|
-
include Blacklist
|
8
|
-
include Whitelist
|
9
8
|
include Streaming
|
9
|
+
include Blocklist
|
10
|
+
include Safelist
|
10
11
|
include Config
|
11
12
|
include Logging
|
12
13
|
include Search
|
14
|
+
include DirectMessages
|
13
15
|
include HomeTimeline
|
14
16
|
include Tweet
|
15
17
|
include Profile
|
@@ -19,9 +21,14 @@ module Chatterbot
|
|
19
21
|
include Followers
|
20
22
|
include UI
|
21
23
|
include Client
|
22
|
-
include DB
|
23
24
|
include Helpers
|
24
25
|
|
26
|
+
# handlers that can use the REST API
|
27
|
+
HANDLER_CALLS = [:direct_messages, :home_timeline, :replies, :search]
|
28
|
+
|
29
|
+
# handlers that require the Streaming API
|
30
|
+
STREAMING_ONLY_HANDLERS = [:favorited, :followed, :deleted]
|
31
|
+
|
25
32
|
#
|
26
33
|
# Create a new bot. No options for now.
|
27
34
|
def initialize(params={})
|
@@ -30,19 +37,117 @@ module Chatterbot
|
|
30
37
|
end
|
31
38
|
|
32
39
|
@config = load_config(params)
|
40
|
+
@run_count = 0
|
41
|
+
|
42
|
+
#
|
43
|
+
# check for command line options
|
44
|
+
# handle resets, etc
|
45
|
+
#
|
46
|
+
|
47
|
+
at_exit do
|
48
|
+
if !@handlers.empty? && @run_count <= 0 && skip_run? != true
|
49
|
+
run_or_stream
|
50
|
+
end
|
51
|
+
|
52
|
+
raise $! if $!
|
53
|
+
end
|
54
|
+
@handlers = {}
|
55
|
+
end
|
33
56
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
57
|
+
#
|
58
|
+
# determine the right API to use and run the bot
|
59
|
+
#
|
60
|
+
def run_or_stream
|
61
|
+
@run_count += 1
|
62
|
+
if streaming?
|
63
|
+
stream!
|
39
64
|
else
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
65
|
+
run!
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
#
|
70
|
+
# run the bot with the Streaming API
|
71
|
+
#
|
72
|
+
def stream!
|
73
|
+
before_run
|
74
|
+
|
75
|
+
#
|
76
|
+
# figure out how we want to call streaming client
|
77
|
+
#
|
78
|
+
if @handlers[:search]
|
79
|
+
method = :filter
|
80
|
+
args = streamify_search_options(@handlers[:search].opts)
|
81
|
+
else
|
82
|
+
method = :user
|
83
|
+
args = nil
|
84
|
+
end
|
85
|
+
|
86
|
+
streaming_client.send(method, args) do |object|
|
87
|
+
handle_streaming_object(object)
|
88
|
+
end
|
89
|
+
after_run
|
90
|
+
end
|
91
|
+
|
92
|
+
#
|
93
|
+
# the REST API and Streaming API have a slightly different format.
|
94
|
+
# tweak our search handler to switch from REST to Streaming
|
95
|
+
#
|
96
|
+
def streamify_search_options(opts)
|
97
|
+
if opts.is_a?(String)
|
98
|
+
{ track: opts }
|
99
|
+
elsif opts.is_a?(Array)
|
100
|
+
{ track: opts.join(", ") }
|
101
|
+
else
|
102
|
+
opts
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
#
|
107
|
+
# run the bot with the REST API
|
108
|
+
#
|
109
|
+
def run!
|
110
|
+
before_run
|
111
|
+
|
112
|
+
HANDLER_CALLS.each { |c|
|
113
|
+
if (h = @handlers[c])
|
114
|
+
puts "calling #{c} #{h.opts.inspect}"
|
115
|
+
send(c, *(h.opts)) do |obj|
|
116
|
+
h.call(obj)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
}
|
120
|
+
|
121
|
+
after_run
|
122
|
+
end
|
123
|
+
|
124
|
+
|
125
|
+
def before_run
|
126
|
+
@run_count = @run_count + 1
|
127
|
+
end
|
128
|
+
|
129
|
+
def after_run
|
130
|
+
|
131
|
+
end
|
132
|
+
|
133
|
+
def call_api_immediately?
|
134
|
+
streaming?
|
135
|
+
end
|
136
|
+
|
137
|
+
def register_handler(method, opts = nil, &block)
|
138
|
+
# @todo raise error if method already defined
|
139
|
+
@handlers[method] = Handler.new(opts, &block)
|
140
|
+
|
141
|
+
if STREAMING_ONLY_HANDLERS.include?(method)
|
142
|
+
puts "Forcing usage of Streaming API to support #{method} calls"
|
143
|
+
self.streaming = true
|
144
|
+
elsif call_api_immediately?
|
145
|
+
h = @handlers[method]
|
146
|
+
send(method, *(h.opts)) do |obj|
|
147
|
+
h.call(obj)
|
148
|
+
end
|
45
149
|
end
|
150
|
+
|
46
151
|
end
|
47
152
|
end
|
48
153
|
end
|
data/lib/chatterbot/client.rb
CHANGED
@@ -6,32 +6,44 @@ module Chatterbot
|
|
6
6
|
# routines for connecting to Twitter and validating the bot
|
7
7
|
#
|
8
8
|
module Client
|
9
|
-
attr_accessor :screen_name, :client, :streaming_client
|
9
|
+
attr_accessor :screen_name, :client, :streaming_client
|
10
10
|
|
11
11
|
#
|
12
12
|
# the main interface to the Twitter API
|
13
13
|
#
|
14
14
|
def client
|
15
|
-
@client ||= Twitter::REST::Client.new(
|
16
|
-
:consumer_key => client_params[:consumer_key],
|
17
|
-
:consumer_secret => client_params[:consumer_secret],
|
18
|
-
:access_token => client_params[:token],
|
19
|
-
:access_token_secret => client_params[:secret]
|
20
|
-
)
|
15
|
+
@client ||= Twitter::REST::Client.new(client_params)
|
21
16
|
end
|
22
17
|
|
18
|
+
#
|
19
|
+
# interace to the Streaming API
|
20
|
+
#
|
23
21
|
def streaming_client
|
24
|
-
@streaming_client ||= Twitter::Streaming::Client.new(
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
22
|
+
@streaming_client ||= Twitter::Streaming::Client.new(client_params)
|
23
|
+
end
|
24
|
+
|
25
|
+
#
|
26
|
+
# return the currently authenticated User
|
27
|
+
#
|
28
|
+
def authenticated_user
|
29
|
+
@user ||= client.user
|
30
30
|
end
|
31
31
|
|
32
|
+
#
|
33
|
+
# reset a few tweet_id trackers
|
34
|
+
#
|
32
35
|
def reset!
|
33
|
-
config[:since_id] =
|
34
|
-
config[:since_id_reply] =
|
36
|
+
config[:since_id] = 1
|
37
|
+
config[:since_id_reply] = 1
|
38
|
+
end
|
39
|
+
|
40
|
+
# reset all since_id counters
|
41
|
+
def reset_since_id_counters
|
42
|
+
reset!
|
43
|
+
reset_since_id
|
44
|
+
reset_since_id_reply
|
45
|
+
reset_since_id_home_timeline
|
46
|
+
reset_since_id_dm
|
35
47
|
end
|
36
48
|
|
37
49
|
#
|
@@ -40,8 +52,10 @@ module Chatterbot
|
|
40
52
|
# the max_id
|
41
53
|
#
|
42
54
|
def reset_since_id
|
43
|
-
config[:
|
44
|
-
|
55
|
+
config[:since_id] = 1
|
56
|
+
# do a search of recent tweets with the letter 'a' in them to
|
57
|
+
# get a rough max tweet id
|
58
|
+
result = client.search("a", since:Time.now - 10).max_by(&:id)
|
45
59
|
update_since_id(result)
|
46
60
|
end
|
47
61
|
|
@@ -49,11 +63,28 @@ module Chatterbot
|
|
49
63
|
# resets the since_id_reply for this bot to the last mention received
|
50
64
|
#
|
51
65
|
def reset_since_id_reply
|
52
|
-
config[:
|
66
|
+
config[:since_id_reply] = 0
|
53
67
|
result = client.mentions_timeline.max_by(&:id)
|
54
68
|
update_since_id_reply(result)
|
55
69
|
end
|
56
|
-
|
70
|
+
|
71
|
+
#
|
72
|
+
# resets the home_timeline_id_reply for this bot to the last tweet
|
73
|
+
# on the timeline
|
74
|
+
#
|
75
|
+
def reset_since_id_home_timeline
|
76
|
+
config[:since_id_reply] = 0
|
77
|
+
result = client.home_timeline.max_by(&:id)
|
78
|
+
update_since_id_home_timeline(result)
|
79
|
+
end
|
80
|
+
|
81
|
+
# reset to the last DM received
|
82
|
+
def reset_since_id_dm
|
83
|
+
config[:since_id_dm] = 0
|
84
|
+
result = client.direct_messages_received.max_by(&:id)
|
85
|
+
update_since_id_dm(result)
|
86
|
+
end
|
87
|
+
|
57
88
|
|
58
89
|
#
|
59
90
|
# the URL we should use for api calls
|
@@ -130,6 +161,7 @@ module Chatterbot
|
|
130
161
|
"#{base_url}#{request.path}?#{params}"
|
131
162
|
end
|
132
163
|
|
164
|
+
# grab a OAuth request token
|
133
165
|
def request_token
|
134
166
|
@request_token ||= consumer.get_request_token
|
135
167
|
end
|
@@ -157,7 +189,7 @@ module Chatterbot
|
|
157
189
|
end
|
158
190
|
|
159
191
|
if needs_auth_token?
|
160
|
-
pin = get_oauth_verifier
|
192
|
+
pin = get_oauth_verifier
|
161
193
|
return false if pin.nil?
|
162
194
|
|
163
195
|
|
data/lib/chatterbot/config.rb
CHANGED
@@ -1,82 +1,115 @@
|
|
1
1
|
module Chatterbot
|
2
|
-
|
2
|
+
|
3
3
|
#
|
4
4
|
# routines for storing config information for the bot
|
5
5
|
module Config
|
6
6
|
attr_accessor :config
|
7
7
|
|
8
|
-
MAX_TWEET_ID = 9223372036854775807
|
9
|
-
COMMAND_LINE_VARIABLES = [:debug_mode, :no_update, :verbose, :reset_since_id]
|
10
|
-
|
11
8
|
#
|
12
9
|
# the entire config for the bot, loaded from YAML files and the DB if applicable
|
13
10
|
def config
|
14
11
|
@config ||= load_config
|
15
12
|
end
|
16
13
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
14
|
+
class << self
|
15
|
+
#
|
16
|
+
# simple boolean attribute generator. define the attribute and a
|
17
|
+
# default value and you get a setter and predicate method
|
18
|
+
#
|
19
|
+
# @param [Symbol] key the key for the variable
|
20
|
+
# @param [Boolean] default default value
|
21
|
+
def attr_boolean(key, default=false)
|
22
|
+
class_eval <<-EVAL
|
23
|
+
attr_writer :#{key.to_s}
|
24
|
+
|
25
|
+
def #{key.to_s}?
|
26
|
+
(@#{key.to_s} == true) || #{default}
|
27
|
+
end
|
28
|
+
EVAL
|
29
|
+
end
|
22
30
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
31
|
+
#
|
32
|
+
# generate a set of methods to manage checks around the
|
33
|
+
# assortment of since_id values we use to track most recent data
|
34
|
+
# retrieve from twitter
|
35
|
+
#
|
36
|
+
# @param [Symbol] key the key for the variable
|
37
|
+
def attr_since_id(key = nil)
|
38
|
+
attr_name = key.nil? ? "since_id" : ["since_id", key.to_s].join("_")
|
39
|
+
class_eval <<-EVAL
|
40
|
+
def #{attr_name}=(x)
|
41
|
+
config[:#{attr_name}] = x
|
42
|
+
end
|
43
|
+
def #{attr_name}
|
44
|
+
config[:#{attr_name}] || 1
|
45
|
+
end
|
46
|
+
|
47
|
+
def update_#{attr_name}(input)
|
48
|
+
max = max_id_from(input)
|
49
|
+
config[:#{attr_name}] = [config[:#{attr_name}].to_i, max.to_i].max
|
50
|
+
end
|
51
|
+
EVAL
|
52
|
+
end
|
27
53
|
end
|
28
54
|
|
29
|
-
#
|
30
|
-
# Check to see if Sequel was loaded successfully. If not, we won't make any DB calls
|
31
|
-
def has_sequel?
|
32
|
-
! defined?(Sequel).nil?
|
33
|
-
end
|
34
55
|
|
56
|
+
attr_boolean :debug_mode, false
|
57
|
+
attr_boolean :verbose, false
|
58
|
+
attr_boolean :streaming, false
|
59
|
+
attr_boolean :skip_run, false
|
60
|
+
attr_boolean :only_interact_with_followers, false
|
61
|
+
|
62
|
+
attr_since_id
|
63
|
+
attr_since_id :home_timeline
|
64
|
+
attr_since_id :reply
|
65
|
+
attr_since_id :dm
|
66
|
+
|
35
67
|
#
|
36
|
-
#
|
37
|
-
|
38
|
-
|
68
|
+
# should we update our config values?
|
69
|
+
# @param [Boolean] val true/false
|
70
|
+
def no_update=(val)
|
71
|
+
config.no_update = val
|
39
72
|
end
|
40
73
|
|
41
|
-
|
42
|
-
|
74
|
+
# should we update our config values?
|
75
|
+
def no_update?
|
76
|
+
config.no_update || false
|
43
77
|
end
|
44
|
-
|
45
|
-
|
46
|
-
|
78
|
+
|
79
|
+
#
|
80
|
+
# return a hash of the params we need to connect to the Twitter API
|
81
|
+
def client_params
|
82
|
+
{
|
83
|
+
:consumer_key => config[:consumer_key],
|
84
|
+
:consumer_secret => config[:consumer_secret],
|
85
|
+
:access_token => config[:access_token],
|
86
|
+
:access_token_secret => config[:access_token_secret]
|
87
|
+
}
|
47
88
|
end
|
48
89
|
|
49
90
|
#
|
50
|
-
#
|
51
|
-
|
52
|
-
|
53
|
-
config[:reset_since_id] || false
|
91
|
+
# do we have an API key specified?
|
92
|
+
def needs_api_key?
|
93
|
+
config[:consumer_key].nil? || config[:consumer_secret].nil?
|
54
94
|
end
|
55
|
-
|
95
|
+
|
56
96
|
#
|
57
|
-
#
|
58
|
-
def
|
59
|
-
config[:
|
97
|
+
# has this script validated with Twitter OAuth?
|
98
|
+
def needs_auth_token?
|
99
|
+
config[:access_token].nil?
|
60
100
|
end
|
61
101
|
|
102
|
+
|
62
103
|
#
|
63
104
|
# Should we run any config updates?
|
64
105
|
def update_config?
|
65
|
-
|
106
|
+
!no_update?
|
66
107
|
end
|
67
108
|
|
68
109
|
#
|
69
110
|
# should we write to a log file?
|
70
111
|
def logging?
|
71
|
-
|
72
|
-
end
|
73
|
-
|
74
|
-
def verbose=(v)
|
75
|
-
config[:verbose] = v
|
76
|
-
end
|
77
|
-
|
78
|
-
def verbose?
|
79
|
-
config[:verbose] || false
|
112
|
+
config[:log_dest] != nil
|
80
113
|
end
|
81
114
|
|
82
115
|
#
|
@@ -86,109 +119,23 @@ module Chatterbot
|
|
86
119
|
end
|
87
120
|
|
88
121
|
#
|
89
|
-
#
|
90
|
-
#
|
91
|
-
def since_id=(x)
|
92
|
-
config[:tmp_since_id] = x
|
93
|
-
end
|
94
|
-
|
95
|
-
#
|
96
|
-
# return the ID of the most recent tweet pulled up in searches
|
97
|
-
def since_id
|
98
|
-
config[:since_id] || 1
|
99
|
-
end
|
100
|
-
|
122
|
+
# given an array or object, return the highest id we can find
|
123
|
+
# @param [Enumerable] s the array to check
|
101
124
|
#
|
102
|
-
# store since_id_reply to a different key so that it doesn't actually
|
103
|
-
# get updated until the bot is done running
|
104
|
-
def since_id_reply=(x)
|
105
|
-
config[:tmp_since_id_reply] = x
|
106
|
-
end
|
107
|
-
|
108
|
-
#
|
109
|
-
# return the ID of the most recent tweet pulled up in mentions or since_id if since_id_reply is nil
|
110
|
-
def since_id_reply
|
111
|
-
config[:since_id_reply] || since_id
|
112
|
-
end
|
113
|
-
|
114
|
-
#
|
115
|
-
# write out our config file
|
116
|
-
def update_config
|
117
|
-
return if ! update_config?
|
118
|
-
|
119
|
-
# don't update flat file if we can store to the DB instead
|
120
|
-
if has_db?
|
121
|
-
debug "storing config to database -- you don't need local file anymore"
|
122
|
-
store_database_config
|
123
|
-
else
|
124
|
-
store_local_config
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
def update_config_at_exit
|
129
|
-
update_config
|
130
|
-
end
|
131
|
-
|
132
125
|
def max_id_from(s)
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
# update the since_id_reply with the id of the given tweet,
|
141
|
-
# unless it is lower thant what we have already
|
142
|
-
#
|
143
|
-
def update_since_id_reply(tweet)
|
144
|
-
return if tweet.nil? or tweet.class != Twitter::Tweet || tweet.id == MAX_TWEET_ID
|
145
|
-
|
146
|
-
tmp_id = tweet.id
|
147
|
-
|
148
|
-
config[:tmp_since_id_reply] = [config[:tmp_since_id_reply].to_i, tmp_id].max
|
149
|
-
end
|
150
|
-
|
151
|
-
#
|
152
|
-
# update the since_id with either the highest ID of the specified
|
153
|
-
# tweets, unless it is lower than what we have already
|
154
|
-
def update_since_id(search)
|
155
|
-
return if search.nil?
|
126
|
+
if ! s.respond_to?(:max)
|
127
|
+
if s.respond_to?(:id)
|
128
|
+
return s.id
|
129
|
+
else
|
130
|
+
return s
|
131
|
+
end
|
132
|
+
end
|
156
133
|
|
157
|
-
tmp_id = if search.is_a?(Twitter::SearchResults)
|
158
|
-
search.attrs[:search_metadata][:max_id]
|
159
|
-
elsif search.respond_to?(:max)
|
160
|
-
max_id_from(search)
|
161
|
-
elsif search.is_a?(Twitter::Tweet)
|
162
|
-
search.id
|
163
|
-
else
|
164
|
-
search
|
165
|
-
end.to_i
|
166
134
|
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
#
|
171
|
-
# return a hash of the params we need to connect to the Twitter API
|
172
|
-
def client_params
|
173
|
-
{
|
174
|
-
:consumer_key => config[:consumer_key],
|
175
|
-
:consumer_secret => config[:consumer_secret],
|
176
|
-
:token => config[:token].nil? ? nil : config[:token],
|
177
|
-
:secret => config[:secret].nil? ? nil : config[:secret]
|
178
|
-
}
|
179
|
-
end
|
180
|
-
|
181
|
-
#
|
182
|
-
# do we have an API key specified?
|
183
|
-
def needs_api_key?
|
184
|
-
config[:consumer_key].nil? || config[:consumer_secret].nil?
|
135
|
+
sorted = s.max { |a, b| a.id.to_i <=> b.id.to_i }
|
136
|
+
sorted && sorted.id
|
185
137
|
end
|
186
138
|
|
187
|
-
#
|
188
|
-
# has this script validated with Twitter OAuth?
|
189
|
-
def needs_auth_token?
|
190
|
-
config[:token].nil?
|
191
|
-
end
|
192
139
|
|
193
140
|
#
|
194
141
|
# determine if we're being called by one of our internal scripts
|
@@ -263,87 +210,17 @@ module Chatterbot
|
|
263
210
|
{
|
264
211
|
:consumer_key => ENV["chatterbot_consumer_key"],
|
265
212
|
:consumer_secret => ENV["chatterbot_consumer_secret"],
|
266
|
-
:
|
267
|
-
:
|
213
|
+
:access_token => ENV["chatterbot_access_token"],
|
214
|
+
:access_token_secret => ENV["chatterbot_access_secret"]
|
268
215
|
}.delete_if { |k, v| v.nil? }.merge(slurp_file(config_file) || {})
|
269
216
|
end
|
270
|
-
|
271
|
-
#
|
272
|
-
# load the config settings from the db, if possible
|
273
|
-
def db_config
|
274
|
-
return {} if db.nil?
|
275
|
-
db[:config][:id => botname]
|
276
|
-
end
|
277
217
|
|
278
|
-
#
|
279
|
-
# figure out what we should save to the local config file. we don't
|
280
|
-
# save anything that exists in the global config, unless it's been modified
|
281
|
-
# for this particular bot.
|
282
|
-
def config_to_save
|
283
|
-
# remove keys that are duped in the global config
|
284
|
-
tmp = config.delete_if { |k, v| global_config.has_key?(k) && global_config[k] == config[k] }
|
285
|
-
|
286
|
-
# let's not store these, they're just command-line options
|
287
|
-
COMMAND_LINE_VARIABLES.each { |k|
|
288
|
-
tmp.delete(k)
|
289
|
-
}
|
290
|
-
|
291
|
-
# update the since_id now
|
292
|
-
tmp[:since_id] = tmp.delete(:tmp_since_id) unless ! tmp.has_key?(:tmp_since_id)
|
293
|
-
tmp[:since_id_reply] = tmp.delete(:tmp_since_id_reply) unless ! tmp.has_key?(:tmp_since_id_reply)
|
294
|
-
|
295
|
-
tmp
|
296
|
-
end
|
297
218
|
|
298
219
|
#
|
299
220
|
# load in the config from the assortment of places it can be specified.
|
300
221
|
def load_config(params={})
|
301
|
-
|
302
|
-
@config
|
303
|
-
@config[:db_uri] ||= ENV["chatterbot_db"] unless ENV["chatterbot_db"].nil?
|
304
|
-
|
305
|
-
# if we have a key to load from the DB, do that now
|
306
|
-
if @config.has_key?(:db_uri) && @config[:db_uri]
|
307
|
-
tmp = db_config
|
308
|
-
@config = @config.merge(tmp) unless tmp.nil?
|
309
|
-
end
|
310
|
-
@config.merge(params)
|
311
|
-
end
|
312
|
-
|
313
|
-
#
|
314
|
-
# write out the config file for this bot
|
315
|
-
def store_local_config
|
316
|
-
File.open(config_file, 'w') { |f| YAML.dump(config_to_save, f) }
|
222
|
+
read_only_data = global_config.merge(bot_config).merge(params)
|
223
|
+
@config = Chatterbot::ConfigManager.new(config_file, read_only_data)
|
317
224
|
end
|
318
|
-
|
319
|
-
#
|
320
|
-
# store config settings in the database, if possible
|
321
|
-
def store_database_config
|
322
|
-
return false if db.nil?
|
323
|
-
|
324
|
-
configs = db[:config]
|
325
|
-
data = {
|
326
|
-
:since_id => config.has_key?(:tmp_since_id) ? config[:tmp_since_id] : config[:since_id],
|
327
|
-
:since_id_reply => config.has_key?(:tmp_since_id_reply) ? config[:tmp_since_id_reply] : config[:since_id_reply],
|
328
|
-
:token => config[:token],
|
329
|
-
:secret => config[:secret],
|
330
|
-
:consumer_secret => config[:consumer_secret],
|
331
|
-
:consumer_key => config[:consumer_key],
|
332
|
-
:updated_at => Time.now #:NOW.sql_function
|
333
|
-
}
|
334
|
-
|
335
|
-
row = configs.filter('id = ?', botname)
|
336
|
-
|
337
|
-
if row.count > 0
|
338
|
-
row.update(data)
|
339
|
-
else
|
340
|
-
data[:id] = botname
|
341
|
-
data[:created_at] = Time.now #:NOW.sql_function
|
342
|
-
configs.insert data
|
343
|
-
end
|
344
|
-
|
345
|
-
true
|
346
|
-
end
|
347
|
-
|
348
225
|
end
|
349
226
|
end
|