tweetskim 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/tweetskim +4 -9
- data/lib/tweetskim.rb +3 -1
- data/lib/tweetskim/core.rb +36 -33
- data/lib/tweetskim/formatter.rb +86 -0
- data/lib/tweetskim/settings.rb +67 -0
- data/lib/tweetskim/twitter_adapter.rb +75 -0
- data/lib/tweetskim/version.rb +1 -1
- data/test/test_tweetskim.rb +0 -44
- metadata +13 -10
data/bin/tweetskim
CHANGED
@@ -14,7 +14,7 @@ def parse_options
|
|
14
14
|
o.on("-h", "--help", "Help page") { puts o; exit }
|
15
15
|
o.on("-m", "--mark-all-read", "Mark everything up to now as read") { |b| options[:mark_all_read] = b }
|
16
16
|
o.on("-n N", "--last-n-tweets N", "Show only the last N tweets") { |n| options[:last_n_tweets] = n }
|
17
|
-
o.on("-
|
17
|
+
o.on("-l", "--html-output", "Output as html") { |b| options[:html_output] = true}
|
18
18
|
#TODO o.on("-u USER", "--user USER", "Which twitter user I am. Example: tweetskim -u thomanil") { |u| options[:user] = u }
|
19
19
|
o.on("-v", "--version", "Spit out version") { |b| puts Tweetskim::VERSION; exit }
|
20
20
|
|
@@ -56,16 +56,11 @@ end
|
|
56
56
|
|
57
57
|
def write_tweets_to_stdout(tweets, options = {})
|
58
58
|
formatter = Tweetskim::Formatter.new
|
59
|
-
output_mode = options[:output_mode]
|
60
59
|
|
61
|
-
if
|
62
|
-
result = formatter.
|
63
|
-
elsif output_mode == "column"
|
64
|
-
result = formatter.column(tweets, {:width => 60})
|
65
|
-
elsif output_mode == "html"
|
66
|
-
puts "Html mode not done yet."; exit
|
60
|
+
if options[:html_output] # Print lines to stdout
|
61
|
+
result = formatter.html(tweets, options)
|
67
62
|
else
|
68
|
-
|
63
|
+
result = formatter.lines(tweets, options)
|
69
64
|
end
|
70
65
|
|
71
66
|
puts result
|
data/lib/tweetskim.rb
CHANGED
data/lib/tweetskim/core.rb
CHANGED
@@ -5,39 +5,6 @@ module Tweetskim
|
|
5
5
|
require 'twitter'
|
6
6
|
require 'oauth'
|
7
7
|
|
8
|
-
|
9
|
-
class Formatter
|
10
|
-
def lines(tweets, options = {})
|
11
|
-
tweet_texts = tweets.reverse.map {|tweet| "--#{tweet.user.name}-- #{text(tweet)}\n"}
|
12
|
-
lines = tweet_texts.join("")
|
13
|
-
end
|
14
|
-
|
15
|
-
def column(tweets, options = {})
|
16
|
-
tweet_texts = tweets.reverse.map {|tweet| "--\\033[1;34m#{tweet.user.name}\\033[0m-- #{text(tweet)}"}
|
17
|
-
reflowed_tweets = tweet_texts.map {|tweet| `echo "#{tweet}" | fmt -w #{options[:width]}` }
|
18
|
-
column = reflowed_tweets.join "\n\n"
|
19
|
-
end
|
20
|
-
|
21
|
-
def pad(column, width)
|
22
|
-
padded_lines = []
|
23
|
-
column.each_line do |line|
|
24
|
-
chopped_line = line.chop
|
25
|
-
padded_lines.push `printf "%-#{width}s\n" "#{chopped_line}"`
|
26
|
-
end
|
27
|
-
padded_lines.join ""
|
28
|
-
end
|
29
|
-
|
30
|
-
def text(tweet)
|
31
|
-
if tweet.retweeted_status
|
32
|
-
"RT @#{tweet.retweeted_status.user.screen_name}: #{tweet.retweeted_status.text}"
|
33
|
-
else
|
34
|
-
tweet.text
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
end
|
39
|
-
|
40
|
-
|
41
8
|
class Settings
|
42
9
|
SETTINGS_TEMPLATE = {:token => nil,
|
43
10
|
:secret => nil,
|
@@ -168,4 +135,40 @@ module Tweetskim
|
|
168
135
|
end
|
169
136
|
end
|
170
137
|
|
138
|
+
|
139
|
+
class Formatter
|
140
|
+
def lines(tweets, options = {})
|
141
|
+
tweet_texts = tweets.reverse.map {|tweet| "--#{tweet.user.name}-- #{text(tweet)}\n"}
|
142
|
+
lines = tweet_texts.join("")
|
143
|
+
end
|
144
|
+
|
145
|
+
def column(tweets, options = {})
|
146
|
+
tweet_texts = tweets.reverse.map {|tweet| "--\\033[1;34m#{tweet.user.name}\\033[0m-- #{text(tweet)}"}
|
147
|
+
reflowed_tweets = tweet_texts.map {|tweet| `echo "#{tweet}" | fmt -w #{options[:width]}` }
|
148
|
+
column = reflowed_tweets.join "\n\n"
|
149
|
+
end
|
150
|
+
|
151
|
+
def html(tweets, options = {})
|
152
|
+
|
153
|
+
end
|
154
|
+
|
155
|
+
def pad(column, width)
|
156
|
+
padded_lines = []
|
157
|
+
column.each_line do |line|
|
158
|
+
chopped_line = line.chop
|
159
|
+
padded_lines.push `printf "%-#{width}s\n" "#{chopped_line}"`
|
160
|
+
end
|
161
|
+
padded_lines.join ""
|
162
|
+
end
|
163
|
+
|
164
|
+
def text(tweet)
|
165
|
+
if tweet.retweeted_status
|
166
|
+
"RT @#{tweet.retweeted_status.user.screen_name}: #{tweet.retweeted_status.text}"
|
167
|
+
else
|
168
|
+
tweet.text
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
|
171
174
|
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
module Tweetskim
|
4
|
+
|
5
|
+
class Formatter
|
6
|
+
|
7
|
+
def lines(tweets, options = {})
|
8
|
+
tweet_texts = tweets.reverse.map {|tweet| "--#{tweet.user.name}-- #{text(tweet)}\n"}
|
9
|
+
lines = tweet_texts.join("")
|
10
|
+
end
|
11
|
+
|
12
|
+
def text(tweet)
|
13
|
+
if tweet.retweeted_status
|
14
|
+
"RT @#{tweet.retweeted_status.user.screen_name}: #{tweet.retweeted_status.text}"
|
15
|
+
else
|
16
|
+
tweet.text
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def htmlize(tweet)
|
21
|
+
text = text(tweet).gsub('\n', '<br/>')
|
22
|
+
"<div class='tweet'><h1>#{tweet.user.name} <span class='time'>#{time(tweet)}</span></h1><p>#{text}<p></div>"
|
23
|
+
end
|
24
|
+
|
25
|
+
def time(tweet)
|
26
|
+
tweet_time = tweet.created_at
|
27
|
+
now = Time.now
|
28
|
+
if now.day != tweet_time.day
|
29
|
+
"#{tweet_time.day} / #{tweet_time.mon}"
|
30
|
+
else
|
31
|
+
"#{tweet_time.hour}:#{tweet_time.min}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def html(tweets, options = {})
|
36
|
+
html_tweets = tweets.reverse.map {|tweet| htmlize(tweet)}.join("")
|
37
|
+
|
38
|
+
body = <<HTML
|
39
|
+
<!DOCTYPE html>
|
40
|
+
<html lang="en">
|
41
|
+
<head>
|
42
|
+
<meta charset="utf-8">
|
43
|
+
<title>Tweetskim</title>
|
44
|
+
</head>
|
45
|
+
<body>
|
46
|
+
<style>
|
47
|
+
#tweets {
|
48
|
+
width: 500px;
|
49
|
+
margin-left: 50px;
|
50
|
+
}
|
51
|
+
|
52
|
+
.tweet {
|
53
|
+
margin-top: 50px;
|
54
|
+
margin-bottom: 50px;
|
55
|
+
}
|
56
|
+
|
57
|
+
.tweet h1 {
|
58
|
+
color: #ADADAD;
|
59
|
+
font-size: 2.0em;
|
60
|
+
margin-bottom: 0px;
|
61
|
+
}
|
62
|
+
|
63
|
+
.tweet .time {
|
64
|
+
color: #ADADAD;
|
65
|
+
font-size: 0.5em;
|
66
|
+
}
|
67
|
+
|
68
|
+
.tweet p {
|
69
|
+
font-size: 1.2em;
|
70
|
+
margin: 0 0 0 0;
|
71
|
+
margin-left: 50px;
|
72
|
+
font-family: sans-serif;
|
73
|
+
}
|
74
|
+
</style>
|
75
|
+
<div id="tweets">
|
76
|
+
#{html_tweets}
|
77
|
+
</div>
|
78
|
+
</body>
|
79
|
+
</html>
|
80
|
+
HTML
|
81
|
+
return body
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
module Tweetskim
|
4
|
+
|
5
|
+
class Settings
|
6
|
+
SETTINGS_TEMPLATE = {:token => nil,
|
7
|
+
:secret => nil,
|
8
|
+
:last_read_status_id => "111111"}
|
9
|
+
|
10
|
+
SETTINGS_FILE_PATH = File.expand_path "~/.tweetskim/default-account"
|
11
|
+
|
12
|
+
require "fileutils"
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
FileUtils.mkdir_p(File.expand_path("~/.tweetskim"))
|
16
|
+
FileUtils.touch SETTINGS_FILE_PATH
|
17
|
+
end
|
18
|
+
|
19
|
+
def load
|
20
|
+
File.open SETTINGS_FILE_PATH, "r" do |filebody|
|
21
|
+
YAML::load(filebody) || SETTINGS_TEMPLATE
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def save(settings)
|
26
|
+
yml_str = YAML::dump(settings)
|
27
|
+
File.open SETTINGS_FILE_PATH, "w" do |filebody|
|
28
|
+
filebody.write yml_str
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def user_credentials_stored?
|
33
|
+
if File.exists? SETTINGS_FILE_PATH
|
34
|
+
settings = load
|
35
|
+
return (settings[:token] && !settings[:token].empty? &&
|
36
|
+
settings[:secret] && !settings[:secret].empty?)
|
37
|
+
else
|
38
|
+
false
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def save_credentials(token, secret)
|
43
|
+
settings = load
|
44
|
+
settings[:token] = token
|
45
|
+
settings[:secret] = secret
|
46
|
+
save settings
|
47
|
+
end
|
48
|
+
|
49
|
+
def load_credentials
|
50
|
+
settings = load
|
51
|
+
return settings[:token], settings[:secret]
|
52
|
+
end
|
53
|
+
|
54
|
+
def load_last_read_status_id
|
55
|
+
settings = load
|
56
|
+
return settings[:last_read_status_id]
|
57
|
+
end
|
58
|
+
|
59
|
+
def save_last_read_status_id(id)
|
60
|
+
settings = load
|
61
|
+
settings[:last_read_status_id] = id
|
62
|
+
save settings
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'twitter'
|
5
|
+
require 'oauth'
|
6
|
+
|
7
|
+
module Tweetskim
|
8
|
+
|
9
|
+
class TwitterAdapter
|
10
|
+
# TODO call for each user in config
|
11
|
+
# implicit for the user authenticated in client. Different user =
|
12
|
+
# different client
|
13
|
+
|
14
|
+
def mentions(tweet_count, since_id)
|
15
|
+
client = authenticated_client
|
16
|
+
mentions = client.mentions({:count => tweet_count.to_i, :since_id => since_id.to_i})
|
17
|
+
end
|
18
|
+
|
19
|
+
def timeline(tweet_count, since_id)
|
20
|
+
client = authenticated_client
|
21
|
+
timeline = client.home_timeline({:count => tweet_count.to_i, :since_id => since_id.to_i})
|
22
|
+
end
|
23
|
+
|
24
|
+
CONSUMER_KEY = "3oUZhYLZcaqqQePajIjnBg"
|
25
|
+
CONSUMER_SECRET = "mAYecEGPwy7BlkibFGHCACtY5x1Mm0YOvczxsll4OY"
|
26
|
+
|
27
|
+
# TODO call for specific user
|
28
|
+
def authenticated_client
|
29
|
+
settings = Tweetskim::Settings.new
|
30
|
+
|
31
|
+
if settings.user_credentials_stored?
|
32
|
+
user_token, user_secret = settings.load_credentials
|
33
|
+
else
|
34
|
+
user_token, user_secret = oauth_pin_dance_for_token_and_secret
|
35
|
+
settings.save_credentials(user_token, user_secret)
|
36
|
+
end
|
37
|
+
|
38
|
+
Twitter.configure do |config|
|
39
|
+
config.consumer_key = CONSUMER_KEY
|
40
|
+
config.consumer_secret = CONSUMER_SECRET
|
41
|
+
config.oauth_token = user_token
|
42
|
+
config.oauth_token_secret = user_secret
|
43
|
+
end
|
44
|
+
|
45
|
+
client = Twitter::Client.new
|
46
|
+
client.verify_credentials
|
47
|
+
return client
|
48
|
+
end
|
49
|
+
|
50
|
+
def oauth_pin_dance_for_token_and_secret
|
51
|
+
oauth_consumer = OAuth::Consumer.new(CONSUMER_KEY, CONSUMER_SECRET,
|
52
|
+
:site => 'http://api.twitter.com',
|
53
|
+
:request_token_path => '/oauth/request_token',
|
54
|
+
:access_token_path => '/oauth/access_token',
|
55
|
+
:authorize_path => '/oauth/authorize')
|
56
|
+
|
57
|
+
request_token = oauth_consumer.get_request_token
|
58
|
+
rtoken = request_token.token
|
59
|
+
rsecret = request_token.secret
|
60
|
+
|
61
|
+
puts "You have to set up Twitter authentication the first time you use tweetskim."
|
62
|
+
puts "Please authenticate by following this URL:"
|
63
|
+
puts request_token.authorize_url
|
64
|
+
|
65
|
+
puts "What was the PIN that Twitter gave you? "
|
66
|
+
pin = gets.chomp
|
67
|
+
|
68
|
+
OAuth::RequestToken.new(oauth_consumer, rtoken, rsecret)
|
69
|
+
access_token = request_token.get_access_token(:oauth_verifier => pin)
|
70
|
+
|
71
|
+
return access_token.token, access_token.secret
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
data/lib/tweetskim/version.rb
CHANGED
data/test/test_tweetskim.rb
CHANGED
@@ -14,16 +14,6 @@ class TestTweetskim < Test::Unit::TestCase
|
|
14
14
|
status.stubs(:user).returns user
|
15
15
|
return status
|
16
16
|
end
|
17
|
-
|
18
|
-
def test_column_creation
|
19
|
-
tweets = [t("Ut enimad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."), t("Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. "), t("Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.")]
|
20
|
-
|
21
|
-
expected = "--\e[1;34mMock User\e[0m-- Excepteur sint\noccaecat cupidatat non proident, sunt\nin culpa qui officia deserunt mollit\nanim id est laborum.\n\n\n--\e[1;34mMock User\e[0m-- Duis aute\nirure dolor in reprehenderit in\nvoluptate velit esse cillum dolore eu\nfugiat nulla pariatur.\n\n\n--\e[1;34mMock User\e[0m-- Ut enimad\nminim veniam, quis nostrud exercitation\nullamco laboris nisi ut aliquip ex ea\ncommodo consequat.\n"
|
22
|
-
|
23
|
-
actual = @f.column(tweets, {:width => 40})
|
24
|
-
|
25
|
-
assert_equal expected, actual
|
26
|
-
end
|
27
17
|
|
28
18
|
def test_line_creation
|
29
19
|
tweets = [t("Ut enimad minim veniam, quis nostrud."), t("Duis aute irure dolor in reprehenderit"), t("Excepteur sint occaecat cupidatat non proident")]
|
@@ -38,40 +28,6 @@ LINES
|
|
38
28
|
|
39
29
|
assert_equal expected, actual
|
40
30
|
end
|
41
|
-
|
42
|
-
|
43
|
-
def test_whitespace_padding
|
44
|
-
|
45
|
-
col = <<COL
|
46
|
-
aaaaaa
|
47
|
-
aaaaa
|
48
|
-
aaaa
|
49
|
-
aaa
|
50
|
-
aa
|
51
|
-
a
|
52
|
-
|
53
|
-
COL
|
54
|
-
|
55
|
-
expected = <<COL
|
56
|
-
aaaaaa
|
57
|
-
aaaaa
|
58
|
-
aaaa
|
59
|
-
aaa
|
60
|
-
aa
|
61
|
-
a
|
62
|
-
|
63
|
-
COL
|
64
|
-
|
65
|
-
assert_equal expected, @f.pad(col, 6)
|
66
|
-
end
|
67
|
-
|
68
|
-
def test_settings_persistence
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
end
|
73
|
-
|
74
|
-
|
75
31
|
|
76
32
|
end
|
77
33
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tweetskim
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-03-
|
12
|
+
date: 2012-03-30 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: twitter
|
16
|
-
requirement: &
|
16
|
+
requirement: &18714980 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *18714980
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: oauth
|
27
|
-
requirement: &
|
27
|
+
requirement: &18714560 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *18714560
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: OptionParser
|
38
|
-
requirement: &
|
38
|
+
requirement: &18714140 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :runtime
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *18714140
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: mocha
|
49
|
-
requirement: &
|
49
|
+
requirement: &18713720 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ! '>='
|
@@ -54,7 +54,7 @@ dependencies:
|
|
54
54
|
version: '0'
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *18713720
|
58
58
|
description: ! 'Usage: tweetskim [options]'
|
59
59
|
email:
|
60
60
|
- thomas@kjeldahlnilsson.net
|
@@ -70,6 +70,9 @@ files:
|
|
70
70
|
- bin/tweetskim
|
71
71
|
- lib/tweetskim.rb
|
72
72
|
- lib/tweetskim/core.rb
|
73
|
+
- lib/tweetskim/formatter.rb
|
74
|
+
- lib/tweetskim/settings.rb
|
75
|
+
- lib/tweetskim/twitter_adapter.rb
|
73
76
|
- lib/tweetskim/version.rb
|
74
77
|
- test/test_helper.rb
|
75
78
|
- test/test_tweetskim.rb
|