twitter-ebooks/bin/ebooks

455 lines
11 KiB
Text
Raw Normal View History

2013-11-08 06:02:05 +11:00
#!/usr/bin/env ruby
2014-05-07 16:45:17 +10:00
# encoding: utf-8
2013-11-08 06:02:05 +11:00
require 'twitter_ebooks'
2014-12-05 16:22:21 +11:00
require 'ostruct'
require 'fileutils'
2013-11-08 06:02:05 +11:00
2014-12-05 22:57:32 +11:00
module Ebooks::Util
def pretty_exception(e)
end
end
2014-12-05 14:03:11 +11:00
module Ebooks::CLI
2013-11-08 06:02:05 +11:00
APP_PATH = Dir.pwd # XXX do some recursive thing instead
2014-12-05 16:22:21 +11:00
HELP = OpenStruct.new
2013-11-08 06:02:05 +11:00
2014-12-05 16:22:21 +11:00
HELP.default = <<STR
Usage:
ebooks help <command>
2014-12-05 16:22:21 +11:00
ebooks new <reponame>
2014-12-08 23:16:47 +11:00
ebooks s[tart]
ebooks c[onsole]
2014-12-05 16:22:21 +11:00
ebooks auth
ebooks consume <corpus_path> [corpus_path2] [...]
2015-02-07 21:12:50 -08:00
ebooks consume-all <model_name> <corpus_path> [corpus_path2] [...]
ebooks append <model_name> <corpus_path>
2014-12-05 16:22:21 +11:00
ebooks gen <model_path> [input]
2014-12-05 22:57:32 +11:00
ebooks archive <username> [path]
2016-02-26 06:18:53 +11:00
ebooks sync <botname> [username]
2014-12-05 16:22:21 +11:00
ebooks tweet <model_path> <botname>
ebooks version
STR
2013-11-08 06:02:05 +11:00
2014-12-05 16:22:21 +11:00
def self.help(command=nil)
if command.nil?
log HELP.default
else
log HELP[command].gsub(/^ {4}/, '')
end
end
HELP.new = <<-STR
Usage: ebooks new <reponame>
Creates a new skeleton repository defining a template bot in
the current working directory specified by <reponame>.
STR
def self.new(reponame)
2013-11-16 07:02:05 -08:00
if reponame.nil?
2014-12-05 16:22:21 +11:00
help :new
exit 1
2013-11-08 06:02:05 +11:00
end
path = "./#{reponame}"
2013-11-08 06:02:05 +11:00
if File.exists?(path)
log "#{path} already exists. Please remove if you want to recreate."
2014-12-05 16:22:21 +11:00
exit 1
2013-11-08 06:02:05 +11:00
end
2014-12-05 22:57:32 +11:00
FileUtils.cp_r(Ebooks::SKELETON_PATH, path)
FileUtils.mv(File.join(path, 'gitignore'), File.join(path, '.gitignore'))
2013-11-08 06:02:05 +11:00
File.open(File.join(path, 'bots.rb'), 'w') do |f|
2014-12-05 22:57:32 +11:00
template = File.read(File.join(Ebooks::SKELETON_PATH, 'bots.rb'))
2013-11-08 06:02:05 +11:00
f.write(template.gsub("{{BOT_NAME}}", reponame))
end
2014-12-05 22:57:32 +11:00
File.open(File.join(path, 'Gemfile'), 'w') do |f|
template = File.read(File.join(Ebooks::SKELETON_PATH, 'Gemfile'))
f.write(template.gsub("{{RUBY_VERSION}}", RUBY_VERSION))
end
2013-11-16 07:02:05 -08:00
log "New twitter_ebooks app created at #{reponame}"
2013-11-08 06:02:05 +11:00
end
2014-12-05 16:22:21 +11:00
HELP.consume = <<-STR
Usage: ebooks consume <corpus_path> [corpus_path2] [...]
2014-12-05 16:22:21 +11:00
Processes some number of text files or json tweet corpuses
2015-02-07 21:12:50 -08:00
into usable models. These will be output at model/<corpus_name>.model
2014-12-05 16:22:21 +11:00
STR
2014-12-05 16:22:21 +11:00
def self.consume(pathes)
if pathes.empty?
2014-12-05 16:22:21 +11:00
help :consume
exit 1
end
2013-11-08 06:02:05 +11:00
pathes.each do |path|
filename = File.basename(path)
shortname = filename.split('.')[0..-2].join('.')
FileUtils.mkdir_p(File.join(APP_PATH, 'model'))
2013-11-08 06:02:05 +11:00
outpath = File.join(APP_PATH, 'model', "#{shortname}.model")
2014-12-05 22:57:32 +11:00
Ebooks::Model.consume(path).save(outpath)
2013-11-27 06:55:14 -08:00
log "Corpus consumed to #{outpath}"
2013-11-08 06:02:05 +11:00
end
end
2014-12-05 16:22:21 +11:00
HELP.consume_all = <<-STR
2015-02-07 21:12:50 -08:00
Usage: ebooks consume-all <model_name> <corpus_path> [corpus_path2] [...]
2014-10-29 18:56:37 +01:00
2014-12-05 16:22:21 +11:00
Processes some number of text files or json tweet corpuses
2015-02-07 21:12:50 -08:00
into one usable model. It will be output at model/<model_name>.model
2014-12-05 16:22:21 +11:00
STR
2014-10-29 18:56:37 +01:00
2014-12-05 16:22:21 +11:00
def self.consume_all(name, paths)
2014-10-29 18:56:37 +01:00
if paths.empty?
2014-12-05 16:22:21 +11:00
help :consume_all
exit 1
2014-10-29 18:56:37 +01:00
end
outpath = File.join(APP_PATH, 'model', "#{name}.model")
2014-12-05 22:57:32 +11:00
Ebooks::Model.consume_all(paths).save(outpath)
2014-10-29 18:56:37 +01:00
log "Corpuses consumed to #{outpath}"
end
HELP.append = <<-STR
Usage: ebooks append <model_name> <corpus_path>
Process then append the provided corpus to the model
instead of overwriting.
STR
def self.append(name, path)
if !name || !path
help :append
exit 1
end
Ebooks::Model.consume(path).append(File.join(APP_PATH,'model',"#{name}.model"))
log "Corpus appended to #{name}.model"
end
HELP.jsonify = <<-STR
Usage: ebooks jsonify <tweets.csv> [tweets.csv2] [...]
Takes a csv twitter archive and converts it to json.
STR
def self.jsonify(paths)
if paths.empty?
log usage
exit
end
paths.each do |path|
name = File.basename(path).split('.')[0]
new_path = name + ".json"
tweets = []
id = nil
if path.split('.')[-1] == "csv" #from twitter archive
csv_archive = CSV.read(path, :headers=>:first_row)
tweets = csv_archive.map do |tweet|
{ text: tweet['text'], id: tweet['tweet_id'] }
end
else
File.read(path).split("\n").each do |l|
if l.start_with?('# ')
id = l.split('# ')[-1]
else
tweet = { text: l }
if id
tweet[:id] = id
id = nil
end
tweets << tweet
end
end
end
File.open(new_path, 'w') do |f|
log "Writing #{tweets.length} tweets to #{new_path}"
f.write(JSON.pretty_generate(tweets))
end
end
end
2014-12-05 16:22:21 +11:00
HELP.gen = <<-STR
Usage: ebooks gen <model_path> [input]
2014-12-05 16:22:21 +11:00
Make a test tweet from the processed model at <model_path>.
Will respond to input if provided.
STR
def self.gen(model_path, input)
if model_path.nil?
2014-12-05 16:22:21 +11:00
help :gen
exit 1
end
2014-12-05 22:57:32 +11:00
model = Ebooks::Model.load(model_path)
2013-11-08 06:02:05 +11:00
if input && !input.empty?
puts "@cmd " + model.make_response(input, 135)
2013-11-08 06:02:05 +11:00
else
puts model.make_statement
2013-11-08 06:02:05 +11:00
end
end
2014-12-05 16:22:21 +11:00
HELP.archive = <<-STR
2014-12-05 22:57:32 +11:00
Usage: ebooks archive <username> [outpath]
2014-12-05 22:57:32 +11:00
Downloads a json corpus of the <username>'s tweets.
Output defaults to corpus/<username>.json
2014-12-05 16:22:21 +11:00
Due to API limitations, this can only receive up to ~3000 tweets
into the past.
The first time you run archive, you will need to enter the auth
details of some account to use for accessing the API. This info
will then be stored in ~/.ebooksrc for later use, and can be
modified there if needed.
2014-12-05 16:22:21 +11:00
STR
2014-12-05 22:57:32 +11:00
def self.archive(username, outpath=nil)
if username.nil?
2014-12-05 16:22:21 +11:00
help :archive
exit 1
end
2014-12-05 22:57:32 +11:00
Ebooks::Archive.new(username, outpath).sync
2013-11-08 06:02:05 +11:00
end
2016-02-26 06:18:53 +11:00
HELP.sync = <<-STR
Usage: ebooks sync <botname> <username>
Copies and flips <username>'s avatar and cover photo, uploading them to <botname>'s profile.
Stores saved avatar's and covers in image/.
STR
def self.sync(botname, username)
if botname.nil?
help :sync
exit 1
end
load File.join(APP_PATH, 'bots.rb')
Ebooks::Sync::run(botname, username)
end
2014-12-05 16:22:21 +11:00
HELP.tweet = <<-STR
Usage: ebooks tweet <model_path> <botname>
2014-04-18 23:37:25 -07:00
2014-12-05 16:22:21 +11:00
Sends a public tweet from the specified bot using text
from the processed model at <model_path>.
STR
2014-04-18 23:37:25 -07:00
2014-12-05 16:22:21 +11:00
def self.tweet(modelpath, botname)
2014-04-18 23:37:25 -07:00
if modelpath.nil? || botname.nil?
2014-12-05 16:22:21 +11:00
help :tweet
exit 1
2014-04-18 23:37:25 -07:00
end
2013-11-08 06:02:05 +11:00
load File.join(APP_PATH, 'bots.rb')
2014-12-05 22:57:32 +11:00
model = Ebooks::Model.load(modelpath)
statement = model.make_statement
2014-12-05 22:57:32 +11:00
bot = Ebooks::Bot.get(botname)
if bot.nil?
log "No such bot configured in bots.rb: #{botname}"
exit 1
end
2013-11-08 06:02:05 +11:00
bot.configure
bot.tweet(statement)
end
2014-12-05 16:22:21 +11:00
HELP.auth = <<-STR
Usage: ebooks auth
Authenticates your Twitter app for any account. By default, will
use the consumer key and secret from the first defined bot. You
can specify another by setting the CONSUMER_KEY and CONSUMER_SECRET
environment variables.
STR
2014-12-05 14:03:11 +11:00
def self.auth
consumer_key, consumer_secret = find_consumer
require 'oauth'
consumer = OAuth::Consumer.new(
consumer_key,
consumer_secret,
site: 'https://twitter.com/',
scheme: :header
)
request_token = consumer.get_request_token
auth_url = request_token.authorize_url()
pin = nil
loop do
log auth_url
log "Go to the above url and follow the prompts, then enter the PIN code here."
print "> "
pin = STDIN.gets.chomp
break unless pin.empty?
end
access_token = request_token.get_access_token(oauth_verifier: pin)
2014-12-05 22:57:32 +11:00
log "Account authorized successfully. Make sure to put these in your bots.rb!\n" +
" bot.access_token = \"#{access_token.token}\"\n" +
" bot.access_token_secret = \"#{access_token.secret}\""
2014-12-05 14:03:11 +11:00
end
2014-12-05 16:22:21 +11:00
HELP.console = <<-STR
Usage: ebooks c[onsole]
Starts an interactive ruby session with your bots loaded
and configured.
STR
def self.console
2014-12-05 14:03:11 +11:00
load_bots
2014-12-05 16:22:21 +11:00
require 'pry'; Ebooks.module_exec { pry }
end
HELP.version = <<-STR
Usage: ebooks version
Shows you twitter_ebooks' version number.
STR
def self.version
require File.expand_path('../../lib/twitter_ebooks/version', __FILE__)
log Ebooks::VERSION
end
2014-12-05 16:22:21 +11:00
HELP.start = <<-STR
Usage: ebooks s[tart] [botname]
Starts running bots. If botname is provided, only runs that bot.
STR
def self.start(botname=nil)
load_bots
if botname.nil?
bots = Ebooks::Bot.all
else
2014-12-05 16:35:57 +11:00
bots = Ebooks::Bot.all.select { |bot| bot.username == botname }
2014-12-05 16:22:21 +11:00
if bots.empty?
log "Couldn't find a defined bot for @#{botname}!"
exit 1
end
end
threads = []
bots.each do |bot|
threads << Thread.new { bot.prepare }
end
threads.each(&:join)
threads = []
bots.each do |bot|
threads << Thread.new do
loop do
begin
bot.start
2014-12-05 22:57:32 +11:00
rescue Exception => e
bot.log e.inspect
puts e.backtrace.map { |s| "\t"+s }.join("\n")
2014-12-05 16:22:21 +11:00
end
bot.log "Sleeping before reconnect"
2014-12-15 08:55:20 +11:00
sleep 60
2014-12-05 16:22:21 +11:00
end
end
end
threads.each(&:join)
2013-11-24 13:16:34 -08:00
end
2014-12-05 14:03:11 +11:00
# Non-command methods
def self.find_consumer
if ENV['CONSUMER_KEY'] && ENV['CONSUMER_SECRET']
log "Using consumer details from environment variables:\n" +
" consumer key: #{ENV['CONSUMER_KEY']}\n" +
" consumer secret: #{ENV['CONSUMER_SECRET']}"
return [ENV['CONSUMER_KEY'], ENV['CONSUMER_SECRET']]
end
load_bots
consumer_key = nil
consumer_secret = nil
Ebooks::Bot.all.each do |bot|
if bot.consumer_key && bot.consumer_secret
consumer_key = bot.consumer_key
consumer_secret = bot.consumer_secret
log "Using consumer details from @#{bot.username}:\n" +
" consumer key: #{bot.consumer_key}\n" +
" consumer secret: #{bot.consumer_secret}\n"
return consumer_key, consumer_secret
end
end
if consumer_key.nil? || consumer_secret.nil?
log "Couldn't find any consumer details to auth an account with.\n" +
"Please either configure a bot with consumer_key and consumer_secret\n" +
"or provide the CONSUMER_KEY and CONSUMER_SECRET environment variables."
2014-12-05 16:22:21 +11:00
exit 1
2014-12-05 14:03:11 +11:00
end
end
def self.load_bots
load 'bots.rb'
if Ebooks::Bot.all.empty?
puts "Couldn't find any bots! Please make sure bots.rb instantiates at least one bot."
end
end
2013-11-08 06:02:05 +11:00
def self.command(args)
if args.length == 0
2014-12-05 16:22:21 +11:00
help
exit 1
2013-11-08 06:02:05 +11:00
end
case args[0]
when "new" then new(args[1])
when "consume" then consume(args[1..-1])
2014-10-29 18:56:37 +01:00
when "consume-all" then consume_all(args[1], args[2..-1])
when "append" then append(args[1],args[2])
2013-11-08 06:02:05 +11:00
when "gen" then gen(args[1], args[2..-1].join(' '))
when "archive" then archive(args[1], args[2])
2016-02-26 06:18:53 +11:00
when "sync" then sync(args[1], args[2])
2013-11-08 06:02:05 +11:00
when "tweet" then tweet(args[1], args[2])
2013-11-27 05:12:54 -08:00
when "jsonify" then jsonify(args[1..-1])
2014-12-05 14:03:11 +11:00
when "auth" then auth
2014-12-05 16:22:21 +11:00
when "console" then console
when "c" then console
2014-12-05 16:35:57 +11:00
when "start" then start(args[1])
when "s" then start(args[1])
2014-12-05 16:22:21 +11:00
when "help" then help(args[1])
when "version" then version
2014-05-06 22:17:57 -07:00
else
2014-12-05 16:22:21 +11:00
log "No such command '#{args[0]}'"
help
2014-05-06 22:17:57 -07:00
exit 1
2013-11-08 06:02:05 +11:00
end
end
end
2014-12-05 14:03:11 +11:00
Ebooks::CLI.command(ARGV)