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 +23 -0
- data/README.markdown +3 -2
- data/Rakefile +3 -2
- data/VERSION.yml +2 -2
- data/bin/tit +48 -9
- data/lib/tit.rb +59 -10
- data/tit.gemspec +7 -4
- metadata +39 -13
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
|
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
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("-
|
35
|
-
|
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
|
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("-
|
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 '
|
4
|
+
require 'fileutils'
|
5
5
|
require 'nokogiri'
|
6
|
-
require 'oauth
|
6
|
+
require 'oauth'
|
7
7
|
require 'time' # heh.
|
8
8
|
require 'yaml'
|
9
|
+
require 'htmlentities' # hate those <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
|
-
|
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
|
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
|
-
|
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.
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
18
|
-
|
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
|
-
|
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
|
-
|
29
|
-
|
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
|
-
|
55
|
+
type: :runtime
|
56
|
+
version_requirements: *id003
|
35
57
|
description: a stupid fucking twitter client
|
36
|
-
email:
|
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.
|
105
|
+
rubygems_version: 1.3.6
|
80
106
|
signing_key:
|
81
107
|
specification_version: 3
|
82
108
|
summary: stupid fucking twitter client
|