twitter_ebooks 2.3.2 → 3.0.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/.gitignore +2 -0
- data/.travis.yml +7 -0
- data/README.md +60 -30
- data/bin/ebooks +239 -117
- data/lib/twitter_ebooks.rb +2 -2
- data/lib/twitter_ebooks/archive.rb +12 -9
- data/lib/twitter_ebooks/bot.rb +343 -109
- data/lib/twitter_ebooks/model.rb +104 -22
- data/lib/twitter_ebooks/nlp.rb +46 -13
- data/lib/twitter_ebooks/suffix.rb +9 -1
- data/lib/twitter_ebooks/version.rb +1 -1
- data/skeleton/Gemfile +1 -1
- data/skeleton/Procfile +1 -1
- data/skeleton/bots.rb +35 -22
- data/spec/bot_spec.rb +178 -0
- data/spec/model_spec.rb +18 -2
- data/twitter_ebooks.gemspec +7 -3
- metadata +72 -20
- data/lib/twitter_ebooks/markov.rb +0 -82
- data/skeleton/run.rb +0 -9
- data/test/corpus/0xabad1dea.tweets +0 -14696
- data/test/keywords.rb +0 -18
- data/test/tokenize.rb +0 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5207fc0b43e38b2aeefa7f995be02cbbdf98f1d2
|
4
|
+
data.tar.gz: 79dd79c4cfeee86ff9bb20e790f19473156cfbba
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: da424019fae0c0b9383be7fcc1feb80a562f3612c6991f49eeda4dd3864c905ce6e3c0497b85ed001ec2eac7d81d2b92a0fd1271bad8f6c539ad017852e8e11f
|
7
|
+
data.tar.gz: e332b40106b7dd9df79704e80ee67fe4798458c8cb483f21e1302dd99f3cfa5a1f6dcf3327654e605c13f42884abc67f3eccd30f4475c7a62e2d7ea882bc894f
|
data/.travis.yml
ADDED
data/README.md
CHANGED
@@ -1,10 +1,26 @@
|
|
1
|
-
# twitter\_ebooks
|
1
|
+
# twitter\_ebooks
|
2
2
|
|
3
|
-
|
3
|
+
[](http://badge.fury.io/rb/twitter_ebooks)
|
4
|
+
[](https://travis-ci.org/mispy/twitter_ebooks)
|
5
|
+
[](https://gemnasium.com/mispy/twitter_ebooks)
|
6
|
+
|
7
|
+
A framework for building interactive twitterbots which respond to mentions/DMs. See [ebooks_example](https://github.com/mispy/ebooks_example) for a fully-fledged bot definition.
|
8
|
+
|
9
|
+
## New in 3.0
|
10
|
+
|
11
|
+
- Bots run in their own threads (no eventmachine), and startup is parallelized
|
12
|
+
- Bots start with `ebooks start`, and no longer die on unhandled exceptions
|
13
|
+
- `ebooks auth` command will create new access tokens, for running multiple bots
|
14
|
+
- `ebooks console` starts a ruby interpreter with bots loaded (see Ebooks::Bot.all)
|
15
|
+
- Replies are slightly rate-limited to prevent infinite bot convos
|
16
|
+
- Non-participating users in a mention chain will be dropped after a few tweets
|
17
|
+
- [API documentation](http://rdoc.info/github/mispy/twitter_ebooks)
|
18
|
+
|
19
|
+
Note that 3.0 is not backwards compatible with 2.x, so upgrade carefully!
|
4
20
|
|
5
21
|
## Installation
|
6
22
|
|
7
|
-
Requires Ruby
|
23
|
+
Requires Ruby 2.0+
|
8
24
|
|
9
25
|
```bash
|
10
26
|
gem install twitter_ebooks
|
@@ -16,48 +32,63 @@ Run `ebooks new <reponame>` to generate a new repository containing a sample bot
|
|
16
32
|
|
17
33
|
``` ruby
|
18
34
|
# This is an example bot definition with event handlers commented out
|
19
|
-
# You can define as many
|
35
|
+
# You can define and instantiate as many bots as you like
|
36
|
+
|
37
|
+
class MyBot < Ebooks::Bot
|
38
|
+
# Configuration here applies to all MyBots
|
39
|
+
def configure
|
40
|
+
# Consumer details come from registering an app at https://dev.twitter.com/
|
41
|
+
# Once you have consumer details, use "ebooks auth" for new access tokens
|
42
|
+
self.consumer_key = '' # Your app consumer key
|
43
|
+
self.consumer_secret = '' # Your app consumer secret
|
20
44
|
|
21
|
-
|
22
|
-
|
23
|
-
# OAuth details can be fetched with https://github.com/marcel/twurl
|
24
|
-
bot.consumer_key = "" # Your app consumer key
|
25
|
-
bot.consumer_secret = "" # Your app consumer secret
|
26
|
-
bot.oauth_token = "" # Token connecting the app to this account
|
27
|
-
bot.oauth_token_secret = "" # Secret connecting the app to this account
|
45
|
+
# Users to block instead of interacting with
|
46
|
+
self.blacklist = ['tnietzschequote']
|
28
47
|
|
29
|
-
|
48
|
+
# Range in seconds to randomize delay when bot.delay is called
|
49
|
+
self.delay_range = 1..6
|
50
|
+
end
|
51
|
+
|
52
|
+
def on_startup
|
53
|
+
scheduler.every '24h' do
|
54
|
+
# Tweet something every 24 hours
|
55
|
+
# See https://github.com/jmettraux/rufus-scheduler
|
56
|
+
# bot.tweet("hi")
|
57
|
+
# bot.pictweet("hi", "cuteselfie.jpg")
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def on_message(dm)
|
30
62
|
# Reply to a DM
|
31
63
|
# bot.reply(dm, "secret secrets")
|
32
64
|
end
|
33
65
|
|
34
|
-
|
66
|
+
def on_follow(user)
|
35
67
|
# Follow a user back
|
36
68
|
# bot.follow(user[:screen_name])
|
37
69
|
end
|
38
70
|
|
39
|
-
|
71
|
+
def on_mention(tweet)
|
40
72
|
# Reply to a mention
|
41
|
-
# bot.reply(tweet, meta[:reply_prefix] + "oh hullo")
|
73
|
+
# bot.reply(tweet, meta(tweet)[:reply_prefix] + "oh hullo")
|
42
74
|
end
|
43
75
|
|
44
|
-
|
76
|
+
def on_timeline(tweet)
|
45
77
|
# Reply to a tweet in the bot's timeline
|
46
|
-
# bot.reply(tweet, meta[:reply_prefix] + "nice tweet")
|
78
|
+
# bot.reply(tweet, meta(tweet)[:reply_prefix] + "nice tweet")
|
47
79
|
end
|
80
|
+
end
|
48
81
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
# bot.pictweet("hi", "cuteselfie.jpg", ":possibly_sensitive => true")
|
54
|
-
end
|
82
|
+
# Make a MyBot and attach it to an account
|
83
|
+
MyBot.new("{{BOT_NAME}}") do |bot|
|
84
|
+
bot.access_token = "" # Token connecting the app to this account
|
85
|
+
bot.access_token_secret = "" # Secret connecting the app to this account
|
55
86
|
end
|
56
87
|
```
|
57
88
|
|
58
|
-
|
89
|
+
'ebooks start' will run all defined bots in their own threads. The easiest way to run bots in a semi-permanent fashion is with [Heroku](https://www.heroku.com); just make an app, push the bot repository to it, enable a worker process in the web interface and it ought to chug along merrily forever.
|
59
90
|
|
60
|
-
The underlying
|
91
|
+
The underlying streaming and REST clients from the [twitter gem](https://github.com/sferik/twitter) can be accessed at `bot.stream` and `bot.twitter` respectively.
|
61
92
|
|
62
93
|
## Archiving accounts
|
63
94
|
|
@@ -92,7 +123,6 @@ Text files use newlines and full stops to seperate statements.
|
|
92
123
|
Once you have a model, the primary use is to produce statements and related responses to input, using a pseudo-Markov generator:
|
93
124
|
|
94
125
|
``` ruby
|
95
|
-
> require 'twitter_ebooks'
|
96
126
|
> model = Ebooks::Model.load("model/0xabad1dea.model")
|
97
127
|
> model.make_statement(140)
|
98
128
|
=> "My Terrible Netbook may be the kind of person who buys Starbucks, but this Rackspace vuln is pretty straight up a backdoor"
|
@@ -103,14 +133,14 @@ Once you have a model, the primary use is to produce statements and related resp
|
|
103
133
|
The secondary function is the "interesting keywords" list. For example, I use this to determine whether a bot wants to fav/retweet/reply to something in its timeline:
|
104
134
|
|
105
135
|
``` ruby
|
106
|
-
top100 = model.keywords.
|
136
|
+
top100 = model.keywords.take(100)
|
107
137
|
tokens = Ebooks::NLP.tokenize(tweet[:text])
|
108
138
|
|
109
139
|
if tokens.find { |t| top100.include?(t) }
|
110
|
-
bot.
|
140
|
+
bot.favorite(tweet[:id])
|
111
141
|
end
|
112
142
|
```
|
113
143
|
|
114
|
-
##
|
144
|
+
## Bot niceness
|
115
145
|
|
116
|
-
|
146
|
+
twitter_ebooks will drop bystanders from mentions for you and avoid infinite bot conversations, but it won't prevent you from doing a lot of other spammy things. Make sure your bot is a good and polite citizen!
|
data/bin/ebooks
CHANGED
@@ -2,54 +2,85 @@
|
|
2
2
|
# encoding: utf-8
|
3
3
|
|
4
4
|
require 'twitter_ebooks'
|
5
|
-
require '
|
5
|
+
require 'ostruct'
|
6
6
|
|
7
|
-
|
7
|
+
module Ebooks::Util
|
8
|
+
def pretty_exception(e)
|
8
9
|
|
9
|
-
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module Ebooks::CLI
|
10
14
|
APP_PATH = Dir.pwd # XXX do some recursive thing instead
|
15
|
+
HELP = OpenStruct.new
|
11
16
|
|
12
|
-
|
13
|
-
|
14
|
-
|
17
|
+
HELP.default = <<STR
|
18
|
+
Usage:
|
19
|
+
ebooks help <command>
|
15
20
|
|
16
|
-
|
17
|
-
|
21
|
+
ebooks new <reponame>
|
22
|
+
ebooks auth
|
23
|
+
ebooks consume <corpus_path> [corpus_path2] [...]
|
24
|
+
ebooks consume-all <corpus_path> [corpus_path2] [...]
|
25
|
+
ebooks gen <model_path> [input]
|
26
|
+
ebooks archive <username> [path]
|
27
|
+
ebooks tweet <model_path> <botname>
|
18
28
|
STR
|
19
29
|
|
30
|
+
def self.help(command=nil)
|
31
|
+
if command.nil?
|
32
|
+
log HELP.default
|
33
|
+
else
|
34
|
+
log HELP[command].gsub(/^ {4}/, '')
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
HELP.new = <<-STR
|
39
|
+
Usage: ebooks new <reponame>
|
40
|
+
|
41
|
+
Creates a new skeleton repository defining a template bot in
|
42
|
+
the current working directory specified by <reponame>.
|
43
|
+
STR
|
44
|
+
|
45
|
+
def self.new(reponame)
|
20
46
|
if reponame.nil?
|
21
|
-
|
22
|
-
exit
|
47
|
+
help :new
|
48
|
+
exit 1
|
23
49
|
end
|
24
50
|
|
25
51
|
path = "./#{reponame}"
|
26
52
|
|
27
53
|
if File.exists?(path)
|
28
54
|
log "#{path} already exists. Please remove if you want to recreate."
|
29
|
-
exit
|
55
|
+
exit 1
|
30
56
|
end
|
31
57
|
|
32
|
-
FileUtils.cp_r(SKELETON_PATH, path)
|
58
|
+
FileUtils.cp_r(Ebooks::SKELETON_PATH, path)
|
33
59
|
|
34
60
|
File.open(File.join(path, 'bots.rb'), 'w') do |f|
|
35
|
-
template = File.read(File.join(SKELETON_PATH, 'bots.rb'))
|
61
|
+
template = File.read(File.join(Ebooks::SKELETON_PATH, 'bots.rb'))
|
36
62
|
f.write(template.gsub("{{BOT_NAME}}", reponame))
|
37
63
|
end
|
38
64
|
|
65
|
+
File.open(File.join(path, 'Gemfile'), 'w') do |f|
|
66
|
+
template = File.read(File.join(Ebooks::SKELETON_PATH, 'Gemfile'))
|
67
|
+
f.write(template.gsub("{{RUBY_VERSION}}", RUBY_VERSION))
|
68
|
+
end
|
69
|
+
|
39
70
|
log "New twitter_ebooks app created at #{reponame}"
|
40
71
|
end
|
41
72
|
|
42
|
-
|
43
|
-
|
44
|
-
Usage: ebooks consume <corpus_path> [corpus_path2] [...]
|
73
|
+
HELP.consume = <<-STR
|
74
|
+
Usage: ebooks consume <corpus_path> [corpus_path2] [...]
|
45
75
|
|
46
|
-
Processes some number of text files or json tweet corpuses
|
47
|
-
into usable models. These will be output at model/<name>.model
|
48
|
-
STR
|
76
|
+
Processes some number of text files or json tweet corpuses
|
77
|
+
into usable models. These will be output at model/<name>.model
|
78
|
+
STR
|
49
79
|
|
80
|
+
def self.consume(pathes)
|
50
81
|
if pathes.empty?
|
51
|
-
|
52
|
-
exit
|
82
|
+
help :consume
|
83
|
+
exit 1
|
53
84
|
end
|
54
85
|
|
55
86
|
pathes.each do |path|
|
@@ -57,24 +88,43 @@ STR
|
|
57
88
|
shortname = filename.split('.')[0..-2].join('.')
|
58
89
|
|
59
90
|
outpath = File.join(APP_PATH, 'model', "#{shortname}.model")
|
60
|
-
Model.consume(path).save(outpath)
|
91
|
+
Ebooks::Model.consume(path).save(outpath)
|
61
92
|
log "Corpus consumed to #{outpath}"
|
62
93
|
end
|
63
94
|
end
|
64
95
|
|
65
|
-
|
66
|
-
|
67
|
-
Usage: ebooks gen <model_path> [input]
|
96
|
+
HELP.consume_all = <<-STR
|
97
|
+
Usage: ebooks consume-all <name> <corpus_path> [corpus_path2] [...]
|
68
98
|
|
69
|
-
|
70
|
-
|
71
|
-
STR
|
99
|
+
Processes some number of text files or json tweet corpuses
|
100
|
+
into one usable model. It will be output at model/<name>.model
|
101
|
+
STR
|
102
|
+
|
103
|
+
def self.consume_all(name, paths)
|
104
|
+
if paths.empty?
|
105
|
+
help :consume_all
|
106
|
+
exit 1
|
107
|
+
end
|
108
|
+
|
109
|
+
outpath = File.join(APP_PATH, 'model', "#{name}.model")
|
110
|
+
Ebooks::Model.consume_all(paths).save(outpath)
|
111
|
+
log "Corpuses consumed to #{outpath}"
|
112
|
+
end
|
113
|
+
|
114
|
+
HELP.gen = <<-STR
|
115
|
+
Usage: ebooks gen <model_path> [input]
|
116
|
+
|
117
|
+
Make a test tweet from the processed model at <model_path>.
|
118
|
+
Will respond to input if provided.
|
119
|
+
STR
|
120
|
+
|
121
|
+
def self.gen(model_path, input)
|
72
122
|
if model_path.nil?
|
73
|
-
|
74
|
-
exit
|
123
|
+
help :gen
|
124
|
+
exit 1
|
75
125
|
end
|
76
126
|
|
77
|
-
model = Model.load(model_path)
|
127
|
+
model = Ebooks::Model.load(model_path)
|
78
128
|
if input && !input.empty?
|
79
129
|
puts "@cmd " + model.make_response(input, 135)
|
80
130
|
else
|
@@ -82,136 +132,208 @@ STR
|
|
82
132
|
end
|
83
133
|
end
|
84
134
|
|
85
|
-
|
86
|
-
|
87
|
-
Usage: ebooks score <model_path> <input>
|
88
|
-
|
89
|
-
Scores "interest" in some text input according to how
|
90
|
-
well unique keywords match the model.
|
91
|
-
STR
|
92
|
-
if model_path.nil? || input.nil?
|
93
|
-
log usage
|
94
|
-
exit
|
95
|
-
end
|
135
|
+
HELP.archive = <<-STR
|
136
|
+
Usage: ebooks archive <username> [outpath]
|
96
137
|
|
97
|
-
|
98
|
-
|
99
|
-
|
138
|
+
Downloads a json corpus of the <username>'s tweets.
|
139
|
+
Output defaults to corpus/<username>.json
|
140
|
+
Due to API limitations, this can only receive up to ~3000 tweets
|
141
|
+
into the past.
|
142
|
+
STR
|
100
143
|
|
101
|
-
def self.archive(username, outpath)
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
Downloads a json corpus of the <username>'s tweets to <outpath>.
|
106
|
-
Due to API limitations, this can only receive up to ~3000 tweets
|
107
|
-
into the past.
|
108
|
-
STR
|
109
|
-
|
110
|
-
if username.nil? || outpath.nil?
|
111
|
-
log usage
|
112
|
-
exit
|
144
|
+
def self.archive(username, outpath=nil)
|
145
|
+
if username.nil?
|
146
|
+
help :archive
|
147
|
+
exit 1
|
113
148
|
end
|
114
149
|
|
115
|
-
Archive.new(username, outpath).sync
|
150
|
+
Ebooks::Archive.new(username, outpath).sync
|
116
151
|
end
|
117
152
|
|
118
|
-
|
119
|
-
|
120
|
-
Usage: ebooks tweet <model_path> <botname>
|
153
|
+
HELP.tweet = <<-STR
|
154
|
+
Usage: ebooks tweet <model_path> <botname>
|
121
155
|
|
122
|
-
Sends a public tweet from the specified bot using text
|
123
|
-
from the processed model at <model_path>.
|
124
|
-
STR
|
156
|
+
Sends a public tweet from the specified bot using text
|
157
|
+
from the processed model at <model_path>.
|
158
|
+
STR
|
125
159
|
|
160
|
+
def self.tweet(modelpath, botname)
|
126
161
|
if modelpath.nil? || botname.nil?
|
127
|
-
|
128
|
-
exit
|
162
|
+
help :tweet
|
163
|
+
exit 1
|
129
164
|
end
|
130
165
|
|
131
166
|
load File.join(APP_PATH, 'bots.rb')
|
132
|
-
model = Model.load(modelpath)
|
167
|
+
model = Ebooks::Model.load(modelpath)
|
133
168
|
statement = model.make_statement
|
134
|
-
|
135
|
-
bot = Bot.get(botname)
|
169
|
+
bot = Ebooks::Bot.get(botname)
|
136
170
|
bot.configure
|
137
171
|
bot.tweet(statement)
|
138
172
|
end
|
139
173
|
|
140
|
-
|
141
|
-
|
142
|
-
Usage: ebooks jsonify <old_corpus_path> [old_corpus_path2] [...]
|
174
|
+
HELP.auth = <<-STR
|
175
|
+
Usage: ebooks auth
|
143
176
|
|
144
|
-
|
145
|
-
|
177
|
+
Authenticates your Twitter app for any account. By default, will
|
178
|
+
use the consumer key and secret from the first defined bot. You
|
179
|
+
can specify another by setting the CONSUMER_KEY and CONSUMER_SECRET
|
180
|
+
environment variables.
|
181
|
+
STR
|
146
182
|
|
147
|
-
|
148
|
-
|
149
|
-
|
183
|
+
def self.auth
|
184
|
+
consumer_key, consumer_secret = find_consumer
|
185
|
+
require 'oauth'
|
186
|
+
|
187
|
+
consumer = OAuth::Consumer.new(
|
188
|
+
consumer_key,
|
189
|
+
consumer_secret,
|
190
|
+
site: 'https://twitter.com/',
|
191
|
+
scheme: :header
|
192
|
+
)
|
193
|
+
|
194
|
+
request_token = consumer.get_request_token
|
195
|
+
auth_url = request_token.authorize_url()
|
196
|
+
|
197
|
+
pin = nil
|
198
|
+
loop do
|
199
|
+
log auth_url
|
200
|
+
|
201
|
+
log "Go to the above url and follow the prompts, then enter the PIN code here."
|
202
|
+
print "> "
|
203
|
+
|
204
|
+
pin = STDIN.gets.chomp
|
205
|
+
|
206
|
+
break unless pin.empty?
|
150
207
|
end
|
151
208
|
|
152
|
-
|
153
|
-
name = File.basename(path).split('.')[0]
|
154
|
-
new_path = name + ".json"
|
209
|
+
access_token = request_token.get_access_token(oauth_verifier: pin)
|
155
210
|
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
211
|
+
log "Account authorized successfully. Make sure to put these in your bots.rb!\n" +
|
212
|
+
" access token: #{access_token.token}\n" +
|
213
|
+
" access token secret: #{access_token.secret}"
|
214
|
+
end
|
215
|
+
|
216
|
+
HELP.console = <<-STR
|
217
|
+
Usage: ebooks c[onsole]
|
218
|
+
|
219
|
+
Starts an interactive ruby session with your bots loaded
|
220
|
+
and configured.
|
221
|
+
STR
|
222
|
+
|
223
|
+
def self.console
|
224
|
+
load_bots
|
225
|
+
require 'pry'; Ebooks.module_exec { pry }
|
226
|
+
end
|
227
|
+
|
228
|
+
HELP.start = <<-STR
|
229
|
+
Usage: ebooks s[tart] [botname]
|
230
|
+
|
231
|
+
Starts running bots. If botname is provided, only runs that bot.
|
232
|
+
STR
|
233
|
+
|
234
|
+
def self.start(botname=nil)
|
235
|
+
load_bots
|
236
|
+
|
237
|
+
if botname.nil?
|
238
|
+
bots = Ebooks::Bot.all
|
239
|
+
else
|
240
|
+
bots = Ebooks::Bot.all.select { |bot| bot.username == botname }
|
241
|
+
if bots.empty?
|
242
|
+
log "Couldn't find a defined bot for @#{botname}!"
|
243
|
+
exit 1
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
threads = []
|
248
|
+
bots.each do |bot|
|
249
|
+
threads << Thread.new { bot.prepare }
|
250
|
+
end
|
251
|
+
threads.each(&:join)
|
252
|
+
|
253
|
+
threads = []
|
254
|
+
bots.each do |bot|
|
255
|
+
threads << Thread.new do
|
256
|
+
loop do
|
257
|
+
begin
|
258
|
+
bot.start
|
259
|
+
rescue Exception => e
|
260
|
+
bot.log e.inspect
|
261
|
+
puts e.backtrace.map { |s| "\t"+s }.join("\n")
|
174
262
|
end
|
263
|
+
bot.log "Sleeping before reconnect"
|
264
|
+
sleep 5
|
175
265
|
end
|
176
266
|
end
|
267
|
+
end
|
268
|
+
threads.each(&:join)
|
269
|
+
end
|
270
|
+
|
271
|
+
# Non-command methods
|
177
272
|
|
178
|
-
|
179
|
-
|
180
|
-
|
273
|
+
def self.find_consumer
|
274
|
+
if ENV['CONSUMER_KEY'] && ENV['CONSUMER_SECRET']
|
275
|
+
log "Using consumer details from environment variables:\n" +
|
276
|
+
" consumer key: #{ENV['CONSUMER_KEY']}\n" +
|
277
|
+
" consumer secret: #{ENV['CONSUMER_SECRET']}"
|
278
|
+
return [ENV['CONSUMER_KEY'], ENV['CONSUMER_SECRET']]
|
279
|
+
end
|
280
|
+
|
281
|
+
load_bots
|
282
|
+
consumer_key = nil
|
283
|
+
consumer_secret = nil
|
284
|
+
Ebooks::Bot.all.each do |bot|
|
285
|
+
if bot.consumer_key && bot.consumer_secret
|
286
|
+
consumer_key = bot.consumer_key
|
287
|
+
consumer_secret = bot.consumer_secret
|
288
|
+
log "Using consumer details from @#{bot.username}:\n" +
|
289
|
+
" consumer key: #{bot.consumer_key}\n" +
|
290
|
+
" consumer secret: #{bot.consumer_secret}\n"
|
291
|
+
return consumer_key, consumer_secret
|
181
292
|
end
|
182
293
|
end
|
294
|
+
|
295
|
+
if consumer_key.nil? || consumer_secret.nil?
|
296
|
+
log "Couldn't find any consumer details to auth an account with.\n" +
|
297
|
+
"Please either configure a bot with consumer_key and consumer_secret\n" +
|
298
|
+
"or provide the CONSUMER_KEY and CONSUMER_SECRET environment variables."
|
299
|
+
exit 1
|
300
|
+
end
|
183
301
|
end
|
184
302
|
|
185
|
-
def self.
|
186
|
-
|
187
|
-
Usage:
|
188
|
-
ebooks new <reponame>
|
189
|
-
ebooks consume <corpus_path> [corpus_path2] [...]
|
190
|
-
ebooks gen <model_path> [input]
|
191
|
-
ebooks score <model_path> <input>
|
192
|
-
ebooks archive <@user> <outpath>
|
193
|
-
ebooks tweet <model_path> <botname>
|
194
|
-
ebooks jsonify <old_corpus_path> [old_corpus_path2] [...]
|
195
|
-
STR
|
303
|
+
def self.load_bots
|
304
|
+
load 'bots.rb'
|
196
305
|
|
306
|
+
if Ebooks::Bot.all.empty?
|
307
|
+
puts "Couldn't find any bots! Please make sure bots.rb instantiates at least one bot."
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
def self.command(args)
|
197
312
|
if args.length == 0
|
198
|
-
|
199
|
-
exit
|
313
|
+
help
|
314
|
+
exit 1
|
200
315
|
end
|
201
316
|
|
202
317
|
case args[0]
|
203
318
|
when "new" then new(args[1])
|
204
319
|
when "consume" then consume(args[1..-1])
|
320
|
+
when "consume-all" then consume_all(args[1], args[2..-1])
|
205
321
|
when "gen" then gen(args[1], args[2..-1].join(' '))
|
206
|
-
when "score" then score(args[1], args[2..-1].join(' '))
|
207
322
|
when "archive" then archive(args[1], args[2])
|
208
323
|
when "tweet" then tweet(args[1], args[2])
|
209
324
|
when "jsonify" then jsonify(args[1..-1])
|
325
|
+
when "auth" then auth
|
326
|
+
when "console" then console
|
327
|
+
when "c" then console
|
328
|
+
when "start" then start(args[1])
|
329
|
+
when "s" then start(args[1])
|
330
|
+
when "help" then help(args[1])
|
210
331
|
else
|
211
|
-
log
|
332
|
+
log "No such command '#{args[0]}'"
|
333
|
+
help
|
212
334
|
exit 1
|
213
335
|
end
|
214
336
|
end
|
215
337
|
end
|
216
338
|
|
217
|
-
Ebooks.command(ARGV)
|
339
|
+
Ebooks::CLI.command(ARGV)
|