tit 1.1.2 → 1.2.3

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/ChangeLog.markdown CHANGED
@@ -1,3 +1,26 @@
1
+ # tit 1.2.2 2011-08-18
2
+
3
+ * freaking <3 all over the fucking twittersphere - decode that shit
4
+
5
+ # tit 1.2.1 2011-08-18
6
+
7
+ * no longer using "-u" or "--update" for sending a tweet when other options exist - using "-t" or "--tweet"
8
+
9
+ # tit 1.2.0 2011-08-18
10
+
11
+ * ability to get user's timeline
12
+ * tweet without using -u: just `tit 'tweet tweet'`
13
+ * user can now set number of tweets printed when requesting timelines
14
+
15
+ # tit 1.1.4 2011-08-17
16
+
17
+ * moved debug from -d to -D
18
+ * added -v --version switch
19
+
20
+ # tit 1.1.3 2011-08-15
21
+
22
+ * fixed a bug - ftools sucks
23
+
1
24
  # tit 1.1.2 2010-01-26
2
25
 
3
26
  * fixed a bug with update times and bad network connections
data/README.markdown CHANGED
@@ -43,15 +43,16 @@ there's some more fucking options for you if you look at `tit -h`
43
43
 
44
44
  ### showing other people your tits ###
45
45
 
46
- $ tit -u "look at my stupid fucking tweet" -G 88.918:-34.879
46
+ $ tit "look at my stupid fucking tweet" -G 88.918:-34.879
47
47
 
48
48
  dependencies
49
49
  ------------
50
50
 
51
- [oauth][] and [nokogiri][]
51
+ [oauth][] and [nokogiri][] and [htmlentities][]
52
52
 
53
53
  [oauth]: http://oauth.rubyforge.org/
54
54
  [nokogiri]: http://nokogiri.org/
55
+ [htmlentities]: http://htmlentities.rubyforge.org/
55
56
 
56
57
  caveats
57
58
  -------
data/Rakefile CHANGED
@@ -4,11 +4,12 @@ begin
4
4
  gemspec.name = "tit"
5
5
  gemspec.summary = "stupid fucking twitter client"
6
6
  gemspec.description = "a stupid fucking twitter client"
7
- gemspec.email = "leif.walsh@gmail.com"
7
+ gemspec.email = ["leif.walsh@gmail.com", "parkrmoore@gmail.com"]
8
8
  gemspec.homepage = "http://github.com/adlaiff6/tit"
9
- gemspec.authors = ["Leif Walsh"]
9
+ gemspec.authors = ["Leif Walsh", "Parker Moore"]
10
10
  gemspec.add_dependency('nokogiri', '>= 0')
11
11
  gemspec.add_dependency('oauth', '>= 0')
12
+ gemspec.add_dependency('htmlentities', '>= 0')
12
13
  end
13
14
  Jeweler::GemcutterTasks.new
14
15
  rescue LoadError
data/VERSION.yml CHANGED
@@ -1,5 +1,5 @@
1
1
  ---
2
- :patch: 2
3
- :minor: 1
2
+ :patch: 3
3
+ :minor: 2
4
4
  :major: 1
5
5
  :build:
data/bin/tit CHANGED
@@ -12,33 +12,45 @@ def main
12
12
  :payload => nil,
13
13
  :notify => nil
14
14
  }
15
+ unchanged = true
15
16
 
16
17
  tit = Tit.new
17
18
 
18
19
  tit.opts = OptionParser.new do |opts|
19
20
  opts.banner = "Usage: #{File.basename($0)} " +
20
- "[options] [action [action options]]"
21
+ "[status] [options] [action [action options]]"
21
22
 
22
23
  opts.separator ""
23
24
  opts.separator "Actions:"
24
-
25
+
25
26
  opts.on("-p", "--public", "Show public timeline") do
26
27
  options[:action] = :public
28
+ unchanged = false
27
29
  end
28
30
  opts.on("-H", "--home", "Show home timeline (default)") do
29
31
  options[:action] = :home
32
+ unchanged = false
30
33
  end
31
34
  opts.on("-m", "--mentions", "Show mentions timeline") do
32
35
  options[:action] = :mentions
36
+ unchanged = false
37
+ end
38
+ opts.on("-u", "--user [USER]",
39
+ "Show a particular user's timeline") do |user|
40
+ unchanged = false
41
+ options[:action] = :user_timeline
42
+ options[:payload] ||= {}
43
+ options[:payload]["user"] = user
33
44
  end
34
- opts.on("-u", "--update [STATUS]",
35
- "Update status (read from STDIN if none given)") do |status|
45
+ opts.on("-t", "--tweet [STATUS]", "Update status (required when using -G)") do |status|
46
+ unchanged = false
36
47
  options[:action] = :update
37
48
  options[:payload] ||= {}
38
- options[:payload]["status"] = status || STDIN
49
+ options[:payload]["status"] = status
39
50
  end
40
51
  opts.on("--pin PIN", ("Set auth pin if this is your first time playing " +
41
52
  "with this tit")) do |pin|
53
+ unchanged = false
42
54
  options[:pin] = pin
43
55
  end
44
56
 
@@ -47,6 +59,7 @@ def main
47
59
 
48
60
  opts.on("-P", "--poll [N]",
49
61
  "Poll for more updates every N secs (default 180)") do |secs|
62
+ unchanged = false
50
63
  options[:wait] = secs || '180'
51
64
  options[:wait] = options[:wait].to_i
52
65
  options[:wait] = 30 if options[:wait] < 30
@@ -57,6 +70,7 @@ def main
57
70
 
58
71
  opts.on("-n", "--notify [PROG]",
59
72
  "Send notifications using PROG (default: notify-send)") do |prog|
73
+ unchanged = false
60
74
  options[:notify] = prog || "notify-send"
61
75
  end
62
76
 
@@ -65,6 +79,7 @@ def main
65
79
 
66
80
  opts.on("-G", "--geo LAT:LONG",
67
81
  "Set latitude and longitude for update") do |s|
82
+ unchanged = false
68
83
  sp = s.split(/:/)
69
84
 
70
85
  tit.abort("invalid geotag format: #{s}", opts) unless sp.length == 2
@@ -77,13 +92,28 @@ def main
77
92
  opts.separator ""
78
93
  opts.separator "Common options:"
79
94
 
80
- opts.on("-d", "--debug", "Show debugging information") do
95
+ opts.on("-D", "--debug", "Show debugging information") do
96
+ unchanged = false
81
97
  options[:debug] = true
82
98
  end
99
+
100
+ opts.on_tail("--count [NUM_STATUSES]", "Set number of statuses you see") do |count|
101
+ unchanged = false
102
+ tit.update_count(count)
103
+ end
104
+
83
105
  opts.on_tail("-h", "--help", "Show this message") do
106
+ unchanged = false
84
107
  puts opts
85
108
  exit
86
109
  end
110
+
111
+ opts.on_tail("-v", "--version", "Show version") do
112
+ unchanged = false
113
+ puts ["v", Tit::VERSION.join('.')].join("")
114
+ exit
115
+ end
116
+
87
117
  end
88
118
 
89
119
  begin
@@ -91,6 +121,13 @@ def main
91
121
  rescue OptionParser::InvalidOption => e
92
122
  tit.abort(e.message)
93
123
  end
124
+
125
+ # terribly hacky, but i can't figure out how OptionsParser handles no switches and just an argument. If it does at all.
126
+ if(unchanged == true and ARGV[0])
127
+ options[:action] = :update
128
+ options[:payload] ||= {}
129
+ options[:payload]["status"] = ARGV[0]
130
+ end
94
131
 
95
132
  if options.include? :pin
96
133
  tit.use_pin(options[:pin])
@@ -99,7 +136,7 @@ def main
99
136
  tit.get_access
100
137
 
101
138
  # check for option errors
102
- if Tit::READERS.include? options[:action]
139
+ if Tit::READERS.include? options[:action] and not options[:action].eql?(:user_timeline)
103
140
  tit.abort("cannot provide geotag when reading") unless options[:payload].nil?
104
141
  tit.abort("cannot notify unless polling") if (options[:wait].nil? and
105
142
  not options[:notify].nil?)
@@ -118,8 +155,9 @@ def main
118
155
  tit.error "got a networking error, are you connected to the intarbutts?"
119
156
  puts e
120
157
  exit(-1)
121
- rescue NoMethodError
158
+ rescue NoMethodError => e
122
159
  tit.error "might have gotten a networking error, check your intarbutts."
160
+ puts e
123
161
  exit(-1)
124
162
  end
125
163
  else
@@ -129,8 +167,9 @@ def main
129
167
  tit.error "got a networking error, are you connected to the intarbutts?"
130
168
  puts e
131
169
  exit(-1)
132
- rescue NoMethodError
170
+ rescue NoMethodError => e
133
171
  tit.error "might have gotten a networking error, check your intarbutts."
172
+ puts e
134
173
  exit(-1)
135
174
  rescue => e
136
175
  tit.error "unknown error"
data/lib/tit.rb CHANGED
@@ -1,13 +1,15 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require 'rubygems'
4
- require 'ftools'
4
+ require 'fileutils'
5
5
  require 'nokogiri'
6
- require 'oauth/consumer'
6
+ require 'oauth'
7
7
  require 'time' # heh.
8
8
  require 'yaml'
9
+ require 'htmlentities' # hate those &lt;3
9
10
 
10
11
  class String
12
+ URI_REGEX = %r"((?:(?:[^ :/?#]+):)(?://(?:[^ /?#]*))(?:[^ ?#]*)(?:\?(?:[^ #]*))?(?:#(?:[^ ]*))?)"
11
13
  def wrapped(cols)
12
14
  curlen = 0
13
15
  split.inject([[]]) do |rows, word|
@@ -20,6 +22,21 @@ class String
20
22
  end
21
23
  end.map { |row| row.join(' ') }
22
24
  end
25
+ def replace_with_expanded_url! (expanded)
26
+ replace(replace_with_expanded_url(expanded))
27
+ end
28
+ def replace_with_expanded_url(expanded)
29
+ replace_uris(/http:\/\/t.co\/[abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXY0123456789]/, expanded)
30
+ end
31
+ def replace_uris(old, newt)
32
+ split(URI_REGEX).collect do |s|
33
+ if s =~ URI_REGEX
34
+ s.gsub(old, newt.join)
35
+ else
36
+ s
37
+ end
38
+ end.join
39
+ end
23
40
  end
24
41
 
25
42
  class Time
@@ -74,16 +91,20 @@ end
74
91
  Why are you reading the documentation, you cunt?
75
92
  =end
76
93
  class Tit
94
+ VERSION = [1, 2, 3]
95
+
96
+ RCFILE = File.join(ENV["HOME"], ".titrc")
77
97
  RTFILE = File.join(ENV["HOME"], ".titrt")
78
98
  ATFILE = File.join(ENV["HOME"], ".titat")
79
99
 
80
- READERS = [:public, :home, :mentions]
100
+ READERS = [:public, :home, :mentions, :user_timeline]
81
101
  WRITERS = [:update]
82
102
 
83
103
  URLS = {
84
104
  :public => "/statuses/public_timeline.xml",
85
105
  :home => "/statuses/home_timeline.xml",
86
106
  :mentions => "/statuses/mentions.xml",
107
+ :user_timeline => "/statuses/user_timeline.xml",
87
108
  :update => "/statuses/update.xml"
88
109
  }
89
110
 
@@ -93,9 +114,10 @@ class Tit
93
114
  def initialize
94
115
  @consumer = OAuth::Consumer.new(KEY, SECRET,
95
116
  { :site => "https://twitter.com" })
96
-
97
117
  # get terminal width
98
118
  @cols = %x[tput cols].to_i
119
+ # get status count
120
+ @prefs = YAML.load_file(RCFILE)
99
121
  end
100
122
  attr_accessor :opts
101
123
 
@@ -111,6 +133,9 @@ class Tit
111
133
  File.open(RTFILE, "w") do |rt|
112
134
  YAML.dump(request_token.params, rt)
113
135
  end
136
+ File.open(RCFILE, "w") do |rc|
137
+ YAML.dump({count: 10}, rc)
138
+ end
114
139
  tuts "Please visit '#{request_token.authorize_url}'."
115
140
  tuts "When you finish, provide your pin with `tit --pin PIN'"
116
141
  exit(0)
@@ -143,12 +168,28 @@ class Tit
143
168
  "revoke your token."
144
169
  end
145
170
 
146
- def get_tits(action)
147
- Nokogiri.XML(@access_token.get(URLS[action]).body).xpath("//status").map do |xml|
171
+ def get_tits(action, payload)
172
+ api_endpoint = URLS[action]
173
+ if(action == :user_timeline and not payload.nil?)
174
+ api_endpoint.concat("?screen_name=".concat(payload['user'])).concat("&count=#{@prefs[:count]}")
175
+ else
176
+ api_endpoint.concat("?count=#{@prefs[:count]}")
177
+ end
178
+ api_endpoint.concat("&include_entities=true")
179
+ coder = HTMLEntities.new
180
+ Nokogiri.XML(@access_token.get(api_endpoint).body).xpath("//status").map do |xml|
148
181
  {
149
182
  :username => xml.at_xpath("./user/name").content,
150
183
  :userid => xml.at_xpath("./user/screen_name").content,
151
- :text => xml.xpath("./text").map { |n| n.content },
184
+ :text => xml.xpath("./text").map do |n|
185
+ txt = coder.decode(n.content)
186
+ if not xml.xpath("./entities/urls").nil?
187
+ xml.xpath("./entities/urls/url").map do |url|
188
+ txt.replace_with_expanded_url! (url.xpath("./expanded_url").map { |expurl| expurl.content })
189
+ end
190
+ end
191
+ txt
192
+ end,
152
193
  :timestamp => Time.parse(xml.at_xpath("./created_at").content),
153
194
  :id => xml.at_xpath("./id").content.to_i,
154
195
  :geo => xml.at_xpath("./geo").instance_eval do
@@ -231,7 +272,7 @@ class Tit
231
272
  def run(options)
232
273
  if READERS.include? options[:action]
233
274
  if options[:wait].nil?
234
- get_tits(options[:action]).reverse.each &method(:show_tit)
275
+ get_tits(options[:action], options[:payload]).reverse.each &method(:show_tit)
235
276
  else
236
277
  poll(options[:wait], options[:action], options[:notify])
237
278
  end
@@ -239,9 +280,17 @@ class Tit
239
280
  update options[:payload]
240
281
  end
241
282
  end
283
+
284
+ def update_count(count)
285
+ @prefs[:count] = count
286
+ File.open(RCFILE, "w") do |rc|
287
+ YAML.dump(@prefs, rc)
288
+ end
289
+ exit(0)
290
+ end
242
291
 
243
292
  def tuts(*strs)
244
- strs.each { |s| puts s.wrapped(@cols) }
293
+ strs.each { |s| puts s.to_s.wrapped(@cols) }
245
294
  end
246
295
 
247
296
  def error(msg)
@@ -250,7 +299,7 @@ class Tit
250
299
 
251
300
  def abort(msg)
252
301
  error(msg)
253
- tuts @opts
302
+ puts @opts
254
303
  exit(-1)
255
304
  end
256
305
  end
data/tit.gemspec CHANGED
@@ -5,17 +5,17 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{tit}
8
- s.version = "1.1.2"
8
+ s.version = "1.2.3"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["Leif Walsh"]
11
+ s.authors = ["Leif Walsh", "Parker Moore"]
12
12
  s.date = %q{2010-01-26}
13
13
  s.default_executable = %q{tit}
14
14
  s.description = %q{a stupid fucking twitter client}
15
- s.email = %q{leif.walsh@gmail.com}
15
+ s.email = [%q{leif.walsh@gmail.com}, %q{parkrmoore@gmail.com}]
16
16
  s.executables = ["tit"]
17
17
  s.extra_rdoc_files = [
18
- "ChangeLog.markdown",
18
+ "ChangeLog.markdown",
19
19
  "LICENSE",
20
20
  "README.markdown"
21
21
  ]
@@ -43,13 +43,16 @@ Gem::Specification.new do |s|
43
43
  if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
44
44
  s.add_runtime_dependency(%q<nokogiri>, [">= 0"])
45
45
  s.add_runtime_dependency(%q<oauth>, [">= 0"])
46
+ s.add_runtime_dependency(%q<htmlentities>, [">= 0"])
46
47
  else
47
48
  s.add_dependency(%q<nokogiri>, [">= 0"])
48
49
  s.add_dependency(%q<oauth>, [">= 0"])
50
+ s.add_dependency(%q<htmlentities>, [">= 0"])
49
51
  end
50
52
  else
51
53
  s.add_dependency(%q<nokogiri>, [">= 0"])
52
54
  s.add_dependency(%q<oauth>, [">= 0"])
55
+ s.add_dependency(%q<htmlentities>, [">= 0"])
53
56
  end
54
57
  end
55
58
 
metadata CHANGED
@@ -1,39 +1,63 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tit
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.2
4
+ prerelease: false
5
+ segments:
6
+ - 1
7
+ - 2
8
+ - 3
9
+ version: 1.2.3
5
10
  platform: ruby
6
11
  authors:
7
12
  - Leif Walsh
13
+ - Parker Moore
8
14
  autorequire:
9
15
  bindir: bin
10
16
  cert_chain: []
11
17
 
12
- date: 2010-01-26 00:00:00 +01:00
18
+ date: 2010-01-26 00:00:00 -05:00
13
19
  default_executable: tit
14
20
  dependencies:
15
21
  - !ruby/object:Gem::Dependency
16
22
  name: nokogiri
17
- type: :runtime
18
- version_requirement:
19
- version_requirements: !ruby/object:Gem::Requirement
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
20
25
  requirements:
21
26
  - - ">="
22
27
  - !ruby/object:Gem::Version
28
+ segments:
29
+ - 0
23
30
  version: "0"
24
- version:
31
+ type: :runtime
32
+ version_requirements: *id001
25
33
  - !ruby/object:Gem::Dependency
26
34
  name: oauth
35
+ prerelease: false
36
+ requirement: &id002 !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ segments:
41
+ - 0
42
+ version: "0"
27
43
  type: :runtime
28
- version_requirement:
29
- version_requirements: !ruby/object:Gem::Requirement
44
+ version_requirements: *id002
45
+ - !ruby/object:Gem::Dependency
46
+ name: htmlentities
47
+ prerelease: false
48
+ requirement: &id003 !ruby/object:Gem::Requirement
30
49
  requirements:
31
50
  - - ">="
32
51
  - !ruby/object:Gem::Version
52
+ segments:
53
+ - 0
33
54
  version: "0"
34
- version:
55
+ type: :runtime
56
+ version_requirements: *id003
35
57
  description: a stupid fucking twitter client
36
- email: leif.walsh@gmail.com
58
+ email:
59
+ - leif.walsh@gmail.com
60
+ - parkrmoore@gmail.com
37
61
  executables:
38
62
  - tit
39
63
  extensions: []
@@ -65,18 +89,20 @@ required_ruby_version: !ruby/object:Gem::Requirement
65
89
  requirements:
66
90
  - - ">="
67
91
  - !ruby/object:Gem::Version
92
+ segments:
93
+ - 0
68
94
  version: "0"
69
- version:
70
95
  required_rubygems_version: !ruby/object:Gem::Requirement
71
96
  requirements:
72
97
  - - ">="
73
98
  - !ruby/object:Gem::Version
99
+ segments:
100
+ - 0
74
101
  version: "0"
75
- version:
76
102
  requirements: []
77
103
 
78
104
  rubyforge_project:
79
- rubygems_version: 1.3.5
105
+ rubygems_version: 1.3.6
80
106
  signing_key:
81
107
  specification_version: 3
82
108
  summary: stupid fucking twitter client