twtr 0.1.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.
data/History.txt ADDED
@@ -0,0 +1,3 @@
1
+ == 0.1.0 2008-05-22
2
+ * 1 major enhancement:
3
+ * Initial release
data/License.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Ryota Maruko
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.txt ADDED
@@ -0,0 +1,90 @@
1
+ = twtr
2
+ * twtr.rubyforge.org
3
+
4
+ == DESCRIPTION:
5
+ twtr is a twitter client for Linux, OSX and Windows Console.
6
+
7
+ == DESCRIPTION:
8
+ twtr is a imprementaion of twitter client.
9
+ Features are
10
+ * Command line application.
11
+ * Simple Twitter library.
12
+ * Http Proxy available.
13
+ * Double Byte Encoding. e.g) ShiftJIS, EUC-JP, etc.
14
+
15
+ == SYNOPSIS:
16
+ * command line
17
+ twtr [subcommand] [options]
18
+ ex)
19
+ $ twtr setting # => (Re)set config file.
20
+ $ twtr ft # => Show friends timeline.
21
+ $ twtr pt -P 2 # => Show public timeline (page 2).
22
+ $ twtr ut -i bob # => Show someone's timeline.
23
+ $ twtr rp # => Show replies.
24
+ $ twtr up -m "hello world" # => Update message.
25
+
26
+ * ruby code
27
+ require "twtr"
28
+ tc = Twtr::Client.new(
29
+ "account" => {"user" => "bob", "password" => "secret"},
30
+ "proxy" => {"host" => "proxy.domain", "port" => 8080},
31
+ )
32
+
33
+ result_json = tc.update({:status => "hello world! from twtr", :format => :json})
34
+
35
+ result_xml = tc.friends_timeline({:page => 2, :format => :xml})
36
+
37
+
38
+ == SUBCMMAND:
39
+ setting (reset) # (Re)Set twtr configuration info.
40
+ friends_timeline (ft) # Show friends timeline.
41
+ putlic_timeline (pt) # Show public timeline.
42
+ user_timeline (ut) # Show user timeline. -i, --id option required.
43
+ replies (rp) # Show replies.
44
+ update (up) # Updates your status. -m, --message option required.
45
+
46
+
47
+ * twtr lib.
48
+ require "twtr"
49
+ tc = Twtr::Client.new(
50
+ Twtr::Config.load("/path/to/twtr.yml")
51
+ )
52
+
53
+ tc.message("hello!")
54
+ #=> [[foo, "hello1"]]
55
+
56
+ tc.timeline(:friends)
57
+ #=> [["foo", "hello!"],["bar","what's up?"],["bar","I'm Home."]]
58
+
59
+ tc.timeline(:timeline)
60
+ #=> [["superman", "hello!"],["badman","what's up?"],["ultraman","I'm Home."],["foo","hello!"]]
61
+
62
+
63
+ == INSTALL:
64
+
65
+ * gem install twtr
66
+
67
+ == LICENSE:
68
+
69
+ (The MIT License)
70
+
71
+ Copyright (c) 2008 Ryota Maruko
72
+
73
+ Permission is hereby granted, free of charge, to any person obtaining
74
+ a copy of this software and associated documentation files (the
75
+ 'Software'), to deal in the Software without restriction, including
76
+ without limitation the rights to use, copy, modify, merge, publish,
77
+ distribute, sublicense, and/or sell copies of the Software, and to
78
+ permit persons to whom the Software is furnished to do so, subject to
79
+ the following conditions:
80
+
81
+ The above copyright notice and this permission notice shall be
82
+ included in all copies or substantial portions of the Software.
83
+
84
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
85
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
86
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
87
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
88
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
89
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
90
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/bin/twtr ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ TWTR_BASE = File.expand_path("#{File.dirname(__FILE__)}/..")
3
+ require "#{TWTR_BASE}/lib/twtr.rb"
4
+
5
+ console = Twtr::Console.new()
6
+ console.run(ARGV)
data/config/hoe.rb ADDED
@@ -0,0 +1,70 @@
1
+ require 'twtr/version'
2
+
3
+ AUTHOR = 'Ryota Maruko' # can also be an array of Authors
4
+ EMAIL = ""
5
+ DESCRIPTION = "twtr is a twitter client for Linux, OSX and Windows Console."
6
+ GEM_NAME = 'twtr' # what ppl will type to install your gem
7
+ RUBYFORGE_PROJECT = 'twtr' # The unix name for your project
8
+ HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
9
+ DOWNLOAD_PATH = "http://rubyforge.org/projects/#{RUBYFORGE_PROJECT}"
10
+
11
+ @config_file = "~/.rubyforge/user-config.yml"
12
+ @config = nil
13
+ RUBYFORGE_USERNAME = "unknown"
14
+ def rubyforge_username
15
+ unless @config
16
+ begin
17
+ @config = YAML.load(File.read(File.expand_path(@config_file)))
18
+ rescue
19
+ puts <<-EOS
20
+ ERROR: No rubyforge config file found: #{@config_file}
21
+ Run 'rubyforge setup' to prepare your env for access to Rubyforge
22
+ - See http://newgem.rubyforge.org/rubyforge.html for more details
23
+ EOS
24
+ exit
25
+ end
26
+ end
27
+ RUBYFORGE_USERNAME.replace @config["username"]
28
+ end
29
+
30
+
31
+ REV = nil
32
+ # UNCOMMENT IF REQUIRED:
33
+ # REV = `svn info`.each {|line| if line =~ /^Revision:/ then k,v = line.split(': '); break v.chomp; else next; end} rescue nil
34
+ VERS = Twtr::VERSION::STRING + (REV ? ".#{REV}" : "")
35
+ RDOC_OPTS = ['--quiet', '--title', 'twtr documentation',
36
+ "--opname", "index.html",
37
+ "--line-numbers",
38
+ "--main", "README",
39
+ "--inline-source"]
40
+
41
+ class Hoe
42
+ def extra_deps
43
+ @extra_deps.reject! { |x| Array(x).first == 'hoe' }
44
+ @extra_deps
45
+ end
46
+ end
47
+
48
+ # Generate all the Rake tasks
49
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
50
+ $hoe = Hoe.new(GEM_NAME, VERS) do |p|
51
+ p.developer(AUTHOR, EMAIL)
52
+ p.description = DESCRIPTION
53
+ p.summary = DESCRIPTION
54
+ p.url = HOMEPATH
55
+ p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
56
+ p.test_globs = ["test/**/test_*.rb"]
57
+ p.clean_globs |= ['**/.*.sw?', '*.gem', '.config', '**/.DS_Store'] #An array of file patterns to delete on clean.
58
+
59
+ # == Optional
60
+ p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
61
+ #p.extra_deps = [] # An array of rubygem dependencies [name, version], e.g. [ ['active_support', '>= 1.3.1'] ]
62
+
63
+ #p.spec_extras = {} # A hash of extra values to set in the gemspec.
64
+
65
+ end
66
+
67
+ CHANGES = $hoe.paragraphs_of('History.txt', 0..1).join("\\n\\n")
68
+ PATH = (RUBYFORGE_PROJECT == GEM_NAME) ? RUBYFORGE_PROJECT : "#{RUBYFORGE_PROJECT}/#{GEM_NAME}"
69
+ $hoe.remote_rdoc_dir = File.join(PATH.gsub(/^#{RUBYFORGE_PROJECT}\/?/,''), 'rdoc')
70
+ $hoe.rsync_args = '-av --delete --ignore-errors'
@@ -0,0 +1,15 @@
1
+ require 'fileutils'
2
+ include FileUtils
3
+
4
+ require 'rubygems'
5
+ %w[rake hoe newgem rubigen].each do |req_gem|
6
+ begin
7
+ require req_gem
8
+ rescue LoadError
9
+ puts "This Rakefile requires the '#{req_gem}' RubyGem."
10
+ puts "Installation: gem install #{req_gem} -y"
11
+ exit
12
+ end
13
+ end
14
+
15
+ $:.unshift(File.join(File.dirname(__FILE__), %w[.. lib]))
data/lib/twtr.rb ADDED
@@ -0,0 +1,17 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ module Twtr
5
+ def self.is_win?
6
+ (RUBY_PLATFORM.downcase =~ /mswin(?!ce)|mingw|cygwin|bccwin/) ? true : false
7
+ end
8
+
9
+ require "kconv"
10
+ require "cgi"
11
+ require "rexml/document"
12
+ require "twtr/version"
13
+ require "twtr/config"
14
+ require "twtr/console"
15
+ require "twtr/client"
16
+ require "twtr/errors"
17
+ end
@@ -0,0 +1,116 @@
1
+ module Twtr
2
+ class Client
3
+ require 'net/http'
4
+
5
+ HOST = "twitter.com"
6
+ PORT = 80
7
+
8
+ DEFAULT_FORMAT = :xml
9
+ DEFAULT_METHOD = :get
10
+
11
+ SEC_SLEEP = 1 # wait update.
12
+
13
+ ATTR_NAMES = [:account_user, :account_password,
14
+ :proxy_host, :proxy_port]
15
+
16
+ ATTR_NAMES.each {|name| attr_accessor name }
17
+
18
+ def initialize(config = nil)
19
+ @account_user = @account_password = @proxy_host = @proxy_port = nil
20
+ ATTR_NAMES.each {|name| load_config(name, config) } if config
21
+ end
22
+
23
+ def load_config(attr_name, config)
24
+ if val = config_from_attr(attr_name, config)
25
+ self.instance_variable_set("@#{attr_name}", val )
26
+ end
27
+ end
28
+
29
+ def config_from_attr(attr_name, config)
30
+ section, name = attr_name.to_s.split("_")
31
+ val ||= (config[section] && config[section][name])
32
+ val ||= (config[section.to_sym] && config[section.to_sym][name.to_sym])
33
+ val
34
+ end
35
+
36
+ def public_timeline(options={})
37
+ get_status("/statuses/public_timeline", options)
38
+ end
39
+
40
+ def friends_timeline(options={})
41
+ get_status("/statuses/friends_timeline", options)
42
+ end
43
+
44
+ def user_timeline(options={})
45
+ id = options.delete(:id) || @account_user
46
+ get_status("/statuses/user_timeline/#{id}", options)
47
+ end
48
+
49
+ def replies(options={})
50
+ get_status("/statuses/replies", options)
51
+ end
52
+
53
+ def update(options={})
54
+ source = post_status("/statuses/update",options)
55
+ friends_timeline()
56
+ end
57
+
58
+ def test(options={})
59
+ request_status("/help/test", options)
60
+ end
61
+
62
+ def post_status(path,options = {})
63
+ options[:method] = :post
64
+ sleep(SEC_SLEEP)
65
+ request_status(path, options)
66
+ end
67
+
68
+ def get_status(path, options = {})
69
+ options[:method] = :get
70
+ request_status(path, options)
71
+ end
72
+
73
+ def request_status(path, options = {})
74
+ format = options.delete(:format) || DEFAULT_FORMAT
75
+ method = options.delete(:method) || DEFAULT_METHOD
76
+ case method
77
+ when :get
78
+ res = get("#{path}.#{format}", options)
79
+ when :post
80
+ res = post("#{path}.#{format}", options)
81
+ end
82
+
83
+ raise Twtr::ResponseErrorException.new("#{res.code}: #{HTTP_ERRORS[res.code]}") \
84
+ if Twtr::HTTP_ERRORS.has_key?(res.code)
85
+ res.body
86
+ end
87
+
88
+ def get(path, params = {})
89
+ query = params.to_a.map {|pair| "#{pair.shift}=#{CGI.escape(pair.pop.to_s)}" if pair.last}
90
+ path = path.gsub(/^#{HOST}/,'')
91
+ path += "?#{query.join('&')}" unless query.empty?
92
+ req = Net::HTTP::Get.new(path)
93
+ request(req)
94
+ end
95
+
96
+ def post(path, params = {})
97
+ req = Net::HTTP::Post.new(path)
98
+ req.set_form_data(params) unless params.empty?
99
+ request(req)
100
+ end
101
+
102
+ def request(req)
103
+ req.basic_auth(@account_user, @account_password) if @account_user
104
+ if(@proxy_host)
105
+ @proxy_host.gsub!(%r|http://|,'')
106
+ Net::HTTP::Proxy(@proxy_host, @proxy_port.to_i).start(HOST, PORT) do |http|
107
+ http.request(req)
108
+ end
109
+ else
110
+ Net::HTTP.start(HOST, PORT){|http| http.request(req)}
111
+ end
112
+ end
113
+
114
+ end
115
+
116
+ end
@@ -0,0 +1,30 @@
1
+ require 'erb'
2
+ require 'yaml'
3
+ require 'yaml/store'
4
+ require 'fileutils'
5
+
6
+
7
+ module Twtr
8
+ module Config
9
+
10
+ def self.load(config_file)
11
+ if File.exist?(config_file)
12
+ config = YAML::load(ERB.new(IO.read(config_file)).result)
13
+ else
14
+ raise Twtr::ConfigFileNotFoundException.new("#{config_file} not found!")
15
+ end
16
+ end
17
+
18
+ def self.save(config_file, config = {})
19
+ FileUtils.mkdir_p(File.dirname(config_file))
20
+ store = YAML::Store.new(config_file)
21
+ store.transaction do |yaml|
22
+ %W(account proxy).each do |name|
23
+ yaml.delete(name) if yaml[name]
24
+ yaml[name] = config[name] if config[name]
25
+ end
26
+ end
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,172 @@
1
+ require "optparse"
2
+
3
+ module Twtr
4
+ DESCRIPTION=<<-_TEXT_
5
+ Twtr Copyright (c) 2008 Ryota Maruko
6
+ DESCRIPTION
7
+ twtr is a imprementaion of twitter client.
8
+ Features are
9
+ * Command line application.
10
+ * Simple Twitter library.
11
+ * Http Proxy available.
12
+ * Double Byte Encoding. e.g) ShiftJIS, EUC-JP, etc.
13
+
14
+ SYNOPSIS
15
+ twtr [subcommand] [options]
16
+ ex)
17
+ $ twtr setting # => (Re)set config file.
18
+ $ twtr ft # => Show friends timeline.
19
+ $ twtr pt -P 2 # => Show public timeline (page 2).
20
+ $ twtr ut -i bob # => Show someone's timeline.
21
+ $ twtr rp # => Show replies.
22
+ $ twtr up -m "hello world" # => Update message.
23
+
24
+ SUBCMMAND
25
+ setting (reset) # (Re)Set twtr configuration info.
26
+ friends_timeline (ft) # Show friends timeline.
27
+ putlic_timeline (pt) # Show public timeline.
28
+ user_timeline (ut) # Show user timeline. -i, --id option required.
29
+ replies (rp) # Show replies.
30
+ update (up) # Updates your status. -m, --message option required.
31
+ _TEXT_
32
+
33
+ class Console
34
+ CONFIG_FILE = Twtr.is_win? ? File.expand_path("#{ENV['HOMEPATH']}/twtr/twtr.yml") \
35
+ : File.expand_path("~/.twtr/twtr.yml")
36
+ DEFAULT_DISPLAY_COUNT = 20
37
+ COUNT_WITH_UPDATE = 5
38
+ DEFAULT_DISPLAY_ENCODE = Twtr.is_win? ? :sjis : :utf8
39
+
40
+ SUBCOMMANDS = {
41
+ :public_timeline => :public_timeline,
42
+ :pt => :public_timeline,
43
+ :friends_timeline => :friends_timeline,
44
+ :ft => :friends_timeline,
45
+ :user_timeline => :user_timeline,
46
+ :ut => :user_timeline,
47
+ :replies => :replies,
48
+ :rp => :replies,
49
+ :update => :update,
50
+ :up => :update,
51
+ :setting => :setting,
52
+ :reset => :setting,
53
+ }
54
+
55
+ def run(args)
56
+ begin
57
+ parse(args)
58
+ rescue Exception => e
59
+ puts e.message
60
+ end
61
+
62
+ if @subcommand
63
+ @configfile ||= CONFIG_FILE
64
+ (edit_config(); return) if @subcommand == :setting
65
+
66
+ begin
67
+ client = Twtr::Client.new(Twtr::Config.load(@configfile))
68
+ show_status( client.send(@subcommand, @params) )
69
+ rescue Twtr::ConfigFileNotFoundException => e
70
+ puts e.message
71
+ edit_config()
72
+ rescue Exception => e
73
+ puts e.message
74
+ end
75
+ end
76
+ end
77
+
78
+ def edit_config()
79
+ puts ">> #{@configfile}"
80
+ print "Edit twtr config? [y/N]:"; replay = gets
81
+ exit if replay.to_s.chomp != 'y'
82
+
83
+ config = {}
84
+
85
+ account = {}
86
+ print "Twitter username: "; user = gets
87
+ print "Twitter password: "; password = gets
88
+ account["user"] = user.to_s.chomp
89
+ account["password"] = password.to_s.chomp
90
+ config["account"] = account
91
+
92
+ proxy = {}
93
+ print "Proxy host (option): "; proxy_host = gets
94
+ print "Proxy port (option): "; proxy_port = gets
95
+ proxy["host"] = proxy_host.chomp if proxy_host && !proxy_host.chomp.empty?
96
+ proxy["port"] = proxy_port.chomp if proxy_port && !proxy_port.chomp.empty?
97
+ config["proxy"] = proxy unless proxy.empty?
98
+
99
+ Twtr::Config.save(@configfile, config)
100
+ end
101
+
102
+ def parse(args)
103
+ @params = {}
104
+ args << '-h' if args.empty?
105
+ args.options do |opt|
106
+
107
+ opt.on('-v','--version','Show the version number and quit') do |val|
108
+ puts("twtr #{Twtr::VERSION::STRING}")
109
+ end
110
+
111
+ opt.on('-h','--help','Show this help message and quit.') do |val|
112
+ show_help(opt)
113
+ end
114
+
115
+ opt.on('-c','--count=number', 'Display number statuses.') do |val|
116
+ @params.merge!({:count => val})
117
+ @display_count = val.to_i
118
+ end
119
+
120
+ opt.on('-i','--id=userid','The ID or screen name of the user.') do |val|
121
+ @params.merge!({:id => val})
122
+ end
123
+
124
+ opt.on('-m','--message=message','Post message.') do |val|
125
+ @display_count ||= COUNT_WITH_UPDATE
126
+ @params.merge!({:source => "twtr", :status => val.toutf8})
127
+ end
128
+
129
+ opt.on('-p', '--page=number',"Gets the 20 next statuses.") do |val|
130
+ @params.merge!({:page => val.to_i})
131
+ end
132
+
133
+ opt.on('-C', '--config=path/to/configfile',"Use another config. (default: #{CONFIG_FILE})") do |val|
134
+ @configfile = val
135
+ end
136
+
137
+ opt.parse!
138
+ end
139
+
140
+ return unless args[0]
141
+ @subcommand = parse_subcommand(args.shift)
142
+ end
143
+
144
+ def parse_subcommand(cmd)
145
+ cmd = cmd.to_sym
146
+ if SUBCOMMANDS.has_key?(cmd)
147
+ @subcommand = SUBCOMMANDS[cmd]
148
+ else
149
+ raise UnknownSubcommandException.new("Unkown Subcommand [#{cmd}]. Please type 'twtr -h'.")
150
+ end
151
+ end
152
+
153
+ def show_help(opt)
154
+ puts "\n#{Twtr::DESCRIPTION}\n#{opt}"
155
+ end
156
+
157
+ def show_status(source)
158
+ @display_count ||= DEFAULT_DISPLAY_COUNT
159
+ doc = REXML::Document.new(source)
160
+ return if(!doc || !doc.elements['statuses'])
161
+ count = 0
162
+ doc.elements['statuses'].each_element do |e|
163
+ msg = e.elements['user/screen_name'].text + ': ' + e.elements['text'].text
164
+ msg = msg.tosjis if Twtr.is_win?
165
+ print "#{msg}\n"
166
+ break if((count+=1) >= @display_count)
167
+ end
168
+ puts ""
169
+ end
170
+
171
+ end
172
+ end