-
-
Save siyo/732069 to your computer and use it in GitHub Desktop.
oreore twitter cli client
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# | |
# this file format is YAML | |
# | |
acount_basic: | |
screen_name1: | |
user: screen_name1 | |
password: password1 | |
screen_name2: | |
user: screen_name2 | |
password: password2 | |
oauth: | |
consumer_key: ov6TGW2mibW0BrJsRfg7DQ | |
consumer_secret: RgaRRSLPAOxt8lcgZCiJP6guaBISg5XbM6ZL8YCnGs | |
access_token_secret: fizz | |
access_token: buzz |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
require 'logger' | |
require 'rubygems' | |
require 'rubytter' | |
require 'tweetstream' | |
require 'pp' | |
require 'yaml_waml' | |
require 'twitpic' | |
require 'time' | |
require 'readline' | |
require 'terminal-color' | |
require 'ruby-growl' | |
require 'observer' | |
class MyOAuth | |
require 'oauth' | |
def initialize(conf) | |
@consumer_key = conf.fetch('consumer_key') | |
@consumer_secret = conf.fetch('consumer_secret') | |
@access_token = conf.fetch('access_token') | |
@access_secret = conf.fetch('access_token_secret') | |
@site = 'http://twitter.com' | |
end | |
def consumer | |
@consmer ||= OAuth::Consumer.new( @consumer_key, | |
@consumer_secret, | |
:site => @site ) | |
end | |
def token | |
@token ||= OAuth::AccessToken.new( consumer, | |
@access_token, | |
@access_secret ) | |
end | |
end | |
class TwStream | |
include Observable | |
def initialize(conf, twitter, options={}) | |
raise ArgumentError unless twitter.is_a? Rubytter | |
@twitter = twitter | |
@logger = Logger.new(STDOUT) | |
@client = TweetStream::Client.new(conf.fetch('user'), conf.fetch('password')) | |
@tl_confs = timeline_confs(options) | |
@client.on_error {|error| abort('Error!' + error) } | |
@client.on_limit {|skip_count| @logger.warn "Skipped #{skip_count}" } | |
end | |
def run | |
query = {} | |
query[:follow] = @tl_confs[:tl_users].inject([]){|s,u| s += u[:ids]} if @tl_confs[:tl_users] | |
query[:track] = @tl_confs[:search_words].map{|e| e[:word]} if @tl_confs[:search_words] | |
@client.filter(query){|st, cl| | |
changed | |
notify_observers(st,@tl_confs) | |
} | |
end | |
private | |
def timeline_confs(options) | |
confs = {} | |
if options.key? :tl_users | |
confs[:tl_users] = [] | |
options[:tl_users].each_with_index{|user,i| | |
tbl = {} | |
tbl[:user] = user | |
tbl[:ids] = @twitter.friends_ids(user) | |
tbl[:index] = i | |
confs[:tl_users] << tbl | |
} | |
end | |
if options.key? :search_words | |
confs[:search_words] = [] | |
options[:search_words].each_with_index{|word,i| | |
tbl = {} | |
tbl[:word] = word | |
tbl[:index] = i | |
confs[:search_words] << tbl | |
} | |
end | |
confs | |
end | |
end | |
class TwStreamStatus | |
def update(status,confs={}) | |
@status = status | |
conf = nil | |
if confs[:tl_users] | |
conf = confs[:tl_users].detect{|e| e[:ids].include?(@status.user.id)} | |
end | |
if confs[:search_words] | |
conf = confs[:search_words].detect{|e| /#{e[:word]}/i =~ @status.text} | |
end | |
return unless conf | |
@conf = conf | |
output | |
end | |
end | |
class TwStreamStatusAnsiTerm < TwStreamStatus | |
COLORS = [:white,:cyan,:magenta,:green,:red,:yellow,:blue] | |
def output | |
puts format_status | |
end | |
def format_status | |
@conf[:user] = 'pub' unless @conf[:user] | |
color = COLORS[@conf[:index] % COLORS.size] | |
strtime = Time.parse(@status.created_at).strftime("%X") | |
"%-24s %s %s %s" % [ (@status.user.screen_name).make_colorized(color), | |
@status.text.gsub(/\n+/," ").make_colorized(color), | |
("at " + strtime).make_colorized(:black), | |
("/ via " + @conf[:user]).make_colorized(:black), ] | |
end | |
end | |
class TwStreamStatusGrowl < TwStreamStatus | |
GROWL_TYPE = "user streams" | |
def initialize | |
@growl = Growl.new('localhost', "Tw streams", [GROWL_TYPE]) | |
end | |
def output | |
via_user = @conf[:user] ? " via #{@conf[:user]}" : "" | |
search_word = @conf[:word] ? " (#{@conf[:word]})" : "" | |
@growl.notify( GROWL_TYPE, | |
@status.user.screen_name + via_user + search_word, | |
@status.text ) | |
end | |
end | |
class Tw | |
require 'optparse' | |
attr_reader :twitter | |
def initialize | |
@conf_path = "#{ENV['HOME']}/.tw" | |
@conf = YAML.load_file(@conf_path) | |
access_token = MyOAuth.new(@conf['oauth']).token | |
@twitter = OAuthRubytter.new(access_token) | |
@api_list = Rubytter.api_settings | |
end | |
def run(argv) | |
api = "" | |
photo = "" | |
disp_format = "yaml" | |
api_list = false | |
stream_users = [] | |
stream_words = [] | |
stream_outputs = ['stdout'] | |
account = "siyo" | |
argv.options{|opt| | |
opt.banner="#{$0} your status [options]" | |
opt.on('-e API','twitter API (syntax: api args...)'){|v| api = v} | |
opt.on('-f format','display format of twitter API result [yaml(default), timeline(or tl)]'){|v| disp_format = v} | |
opt.on('-p photo','post image file to twitpic'){|v| photo = v} | |
opt.on('-a', '--account ACCOUNT',''){|v| account = v} | |
opt.on('--stream-user USER1, USER2...','following ids of specific user streams. '){|v| stream_users = v.split(",")} | |
opt.on('--stream-word WORD1, WORD2...','search words in user streams.[e.g. stdout,growl]'){|v| stream_words = v.split(",")} | |
opt.on('--stream-output OUT1,OUTP2...',''){|v| stream_outputs = v.split(",")} | |
opt.on('--apilist','display twitter API list'){ | |
@api_list.each{|e| puts "%-24s ... %s" % e } | |
exit | |
} | |
opt.on('-d','--diff-followers', 'diff saved followers ids in ~/.tw'){ | |
diff_followers_ids | |
exit | |
} | |
opt.parse! | |
} | |
if not ( stream_users.empty? && stream_words.empty?) | |
options = {} | |
options[:tl_users] = stream_users unless stream_users.empty? | |
options[:search_words] = stream_words unless stream_words.empty? | |
stream = TwStream.new(@conf['account_basic'][account],@twitter,options) | |
stream.add_observer(TwStreamStatusAnsiTerm.new) if stream_outputs.detect{|e| /(stdout|term)/i =~ e} | |
stream.add_observer(TwStreamStatusGrowl.new) if stream_outputs.detect{|e| /growl/i =~ e} | |
stream.run | |
exit | |
end | |
if not api.empty? | |
send_api(api, argv, disp_format) | |
end | |
if not photo.empty? | |
conf = @conf['account_basic'][account] | |
argv << to_twitpic(conf['user'],conf['password'],photo) | |
end | |
if argv.empty? | |
ARGF.each{|e| | |
next if e.empty? | |
puts e | |
@twitter.update e | |
} | |
else | |
puts str = argv.join(" ") | |
@twitter.update( str ) | |
end | |
end | |
private | |
def send_api(api, args, disp_format='yaml') | |
raise "'#{api}' is not API." unless @api_list.map{|e| e[0]}.include? api | |
ret = @twitter.send(api, *args) | |
exit unless ret | |
yaml = YAML.dump(ret) | |
puts yaml2disp_format(yaml, disp_format) | |
exit | |
end | |
def to_twitpic(user,password,file) | |
ret = TwitPic.new(user,password).upload(file) | |
ret[:mediaurl] | |
end | |
def yaml2disp_format(yaml, disp_format) | |
case disp_format | |
when 'yaml','yml' | |
yaml | |
when 'timeline','tl' | |
str = "" | |
YAML.load(yaml).each_with_index{|e,i| | |
str << "%-2d %s: %s / %s\n" % [ i + 1, | |
e[:user][:screen_name], | |
e[:text].gsub("\n", " "), | |
Time.parse(e[:created_at]).strftime("%X") ] | |
} | |
str | |
else | |
raise "invalid display format : '#{format}'" | |
end | |
end | |
def save_followers_ids | |
ids = @twitter.followers_ids(@twitter.login) | |
@conf['followers_ids'] = ids | |
File.open(@conf_path,"wb"){|f| f.write(YAML.dump(@conf))} | |
end | |
def diff_followers_ids | |
saved_ids = @conf['followers_ids'] | |
current_ids = @twitter.followers_ids(@twitter.login) | |
puts "number of followers : %d -> %d" % [saved_ids.size, current_ids.size] | |
proc = Proc.new{|id| | |
begin | |
str = @twitter.user(id).screen_name | |
rescue => e | |
str = e.to_s | |
end | |
str | |
} | |
str = (current_ids - saved_ids).inject(""){ |s,e| s << "+ %s\n" % [proc.call(e)] } | |
str = (saved_ids - current_ids).inject(str){|s,e| s << "- %s\n" % [proc.call(e)] } | |
if str.empty? | |
return | |
else | |
puts str | |
end | |
while buf = Readline.readline("save followers ids? : [y/N] ") | |
break if buf.empty? | |
case buf | |
when /y/i | |
save_followers_ids | |
puts "saved." | |
break | |
when /n/i | |
break | |
else | |
next | |
end | |
end | |
end | |
end | |
Tw.new.run(ARGV) if __FILE__ == $0 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment