tweetwine 0.2.11 → 0.2.12
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.rdoc +7 -0
- data/{MIT-LICENSE.txt → LICENSE.txt} +1 -1
- data/README.md +122 -0
- data/Rakefile +59 -78
- data/bin/tweetwine +1 -0
- data/example/example_helper.rb +3 -1
- data/example/search_statuses_example.rb +2 -0
- data/example/show_followers_example.rb +2 -0
- data/example/show_friends_example.rb +2 -0
- data/example/show_home_example.rb +2 -0
- data/example/show_mentions_example.rb +2 -0
- data/example/show_metadata_example.rb +2 -0
- data/example/show_user_example.rb +2 -0
- data/example/update_status_example.rb +2 -0
- data/example/use_http_proxy_example.rb +2 -0
- data/lib/tweetwine/cli.rb +2 -0
- data/lib/tweetwine/client.rb +2 -0
- data/lib/tweetwine/io.rb +16 -11
- data/lib/tweetwine/meta.rb +3 -1
- data/lib/tweetwine/options.rb +2 -0
- data/lib/tweetwine/retrying_http.rb +2 -0
- data/lib/tweetwine/startup_config.rb +2 -0
- data/lib/tweetwine/url_shortener.rb +2 -0
- data/lib/tweetwine/util.rb +9 -6
- data/lib/tweetwine.rb +2 -0
- data/man/tweetwine.1 +109 -0
- data/man/tweetwine.1.ronn +69 -0
- data/man/tweetwine.7 +216 -0
- data/man/tweetwine.7.ronn +122 -0
- data/test/cli_test.rb +3 -1
- data/test/client_test.rb +3 -1
- data/test/io_test.rb +38 -31
- data/test/options_test.rb +3 -1
- data/test/retrying_http_test.rb +11 -11
- data/test/startup_config_test.rb +4 -2
- data/test/test_helper.rb +57 -47
- data/test/url_shortener_test.rb +3 -1
- data/test/util_test.rb +10 -3
- metadata +145 -20
- data/README.rdoc +0 -117
- /data/example/{fixtures → fixture}/home.json +0 -0
- /data/example/{fixtures → fixture}/mentions.json +0 -0
- /data/example/{fixtures → fixture}/search.json +0 -0
- /data/example/{fixtures → fixture}/update.json +0 -0
- /data/example/{fixtures → fixture}/user.json +0 -0
- /data/example/{fixtures → fixture}/users.json +0 -0
- /data/test/{fixtures → fixture}/test_config.yaml +0 -0
data/CHANGELOG.rdoc
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
=== 0.2.12 released 2010-04-17
|
2
|
+
|
3
|
+
* Since this is a command line application, provide documentation as gem
|
4
|
+
manual
|
5
|
+
* Fix highlighting of URLs when partial URLs match
|
6
|
+
* Improve test code structure
|
7
|
+
|
1
8
|
=== 0.2.11 released 2010-02-28
|
2
9
|
|
3
10
|
* Fix compatibility with rest-client 1.4.0
|
data/README.md
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
tweetwine -- a simple Twitter command line agent
|
2
|
+
================================================
|
3
|
+
|
4
|
+
## DESCRIPTION
|
5
|
+
|
6
|
+
Tweetwine supports showing the home timeline of the authenticated user, the
|
7
|
+
latest statuses of friends and followers, and the latest statuses that mention
|
8
|
+
the user. If that's not enough, statuses can be searched with arbitrary terms.
|
9
|
+
In addition, new statuses can be sent.
|
10
|
+
|
11
|
+
Features:
|
12
|
+
|
13
|
+
* Simple to use command line interface, with Bash completion support
|
14
|
+
* ANSI coloring of statuses, but in discreet manner
|
15
|
+
* Supports shortening URLs in a status update with a configurable shortening
|
16
|
+
service
|
17
|
+
* Configuration file for preferred settings
|
18
|
+
|
19
|
+
## INSTALL
|
20
|
+
|
21
|
+
Install Tweetwine with RubyGems:
|
22
|
+
|
23
|
+
$ gem install tweetwine
|
24
|
+
|
25
|
+
The program is compatible with both Ruby 1.8 and 1.9.
|
26
|
+
|
27
|
+
The program requires [rest-client](http://github.com/archiloque/rest-client)
|
28
|
+
gem to be installed. In addition, the program needs
|
29
|
+
[json](http://json.rubyforge.org/) gem on Ruby 1.8.
|
30
|
+
|
31
|
+
Documentation is provided as gem man pages. Use
|
32
|
+
[gem-man](http://github.com/defunkt/gem-man) to see them:
|
33
|
+
|
34
|
+
$ gem man tweetwine
|
35
|
+
|
36
|
+
## BASIC USAGE AND CONFIGURATION
|
37
|
+
|
38
|
+
In the command line, run the program by entering
|
39
|
+
|
40
|
+
$ tweetwine [ <GLOBAL_OPTIONS> ] [ <COMMAND> ] [ <COMMAND_OPTIONS> ]
|
41
|
+
|
42
|
+
The program needs the user's username and password for authentication. This
|
43
|
+
information can be supplied either via a configuration file or as an option
|
44
|
+
(`-a USERNAME:PASSWORD`) to the program. It is recommended to use the former
|
45
|
+
method over the latter.
|
46
|
+
|
47
|
+
The configuration file, in `~/.tweetwine`, is in YAML syntax. The program
|
48
|
+
recognizes the following basic settings:
|
49
|
+
|
50
|
+
username: <your_username>
|
51
|
+
password: <your_password>
|
52
|
+
colors: true|false
|
53
|
+
|
54
|
+
For all the global options and commands, see:
|
55
|
+
|
56
|
+
$ tweetwine help
|
57
|
+
|
58
|
+
For information about a specific command and its options, enter:
|
59
|
+
|
60
|
+
$ tweetwine help <COMMAND>
|
61
|
+
|
62
|
+
### URL shortening for status update
|
63
|
+
|
64
|
+
Before actually sending a status update, it is possible for the software to
|
65
|
+
shorten the URLs in the update by using an external web service. This can be
|
66
|
+
enabled via the `shorten_urls` key in configuration file; for example:
|
67
|
+
|
68
|
+
username: spoonman
|
69
|
+
password: withyourhands
|
70
|
+
colors: true
|
71
|
+
shorten_urls:
|
72
|
+
enable: true
|
73
|
+
service_url: http://is.gd/create.php
|
74
|
+
method: post
|
75
|
+
url_param_name: URL
|
76
|
+
xpath_selector: //input[@id='short_url']/@value
|
77
|
+
|
78
|
+
The supported methods (in `method`) are `get` and `post`. The method chosen
|
79
|
+
affects whether parameters are passed as URL query parameters or as payload
|
80
|
+
in the HTTP request, respectively. Extra parameters can be given via
|
81
|
+
`extra_params` key, as a hash.
|
82
|
+
|
83
|
+
The `xpath_selector` is needed to extract the shortened URL from the result.
|
84
|
+
|
85
|
+
URL shortening can be disabled by
|
86
|
+
|
87
|
+
* not defining `shorten_urls` key in the configuration file,
|
88
|
+
* setting key `enable` to `false`, or
|
89
|
+
* using the command line option `--no-url-shorten`.
|
90
|
+
|
91
|
+
*NOTE:* The use of the feature requires [nokogiri](http://nokogiri.org/) gem
|
92
|
+
to be installed.
|
93
|
+
|
94
|
+
### HTTP proxy setting
|
95
|
+
|
96
|
+
If `$http_proxy` environment variable is set, Tweetwine attempts to use the
|
97
|
+
URL in the environment variable as HTTP proxy for all its HTTP connections.
|
98
|
+
This setting can be overridden with `--http-proxy` and `--no-http-proxy`
|
99
|
+
command line options.
|
100
|
+
|
101
|
+
### Bash command line completion support
|
102
|
+
|
103
|
+
Bash shell supports command line completion via tab character. If you want to
|
104
|
+
enable Tweetwine specific completion with Bash, source the file
|
105
|
+
`tweetwine-completion.bash`, located in `contrib` directory:
|
106
|
+
|
107
|
+
. contrib/tweetwine-completion.bash
|
108
|
+
|
109
|
+
In order to do this automatically when your shell starts, insert the following
|
110
|
+
snippet to your Bash initialization script (such as `~/.bashrc`):
|
111
|
+
|
112
|
+
if [ -f <path_to_tweetwine>/contrib/tweetwine-completion.bash ]; then
|
113
|
+
. <path_to_tweetwine>/contrib/tweetwine-completion.bash
|
114
|
+
fi
|
115
|
+
|
116
|
+
## COPYRIGHT
|
117
|
+
|
118
|
+
Tweetwine is Copyright (c) 2009-2010 Tuomas Kareinen
|
119
|
+
|
120
|
+
## SEE ALSO
|
121
|
+
|
122
|
+
tweetwine(1), <http://github.com/tuomas/tweetwine>
|
data/Rakefile
CHANGED
@@ -1,94 +1,75 @@
|
|
1
|
-
|
1
|
+
# coding: utf-8
|
2
2
|
|
3
|
-
require "
|
3
|
+
require "rake/clean"
|
4
4
|
|
5
|
+
$LOAD_PATH.unshift(File.expand_path("../lib/", __FILE__))
|
5
6
|
name = "tweetwine"
|
6
|
-
require "#{name}"
|
7
|
+
require "#{name}/meta"
|
7
8
|
version = Tweetwine::VERSION.dup
|
8
9
|
|
9
|
-
|
10
|
+
namespace :gem do
|
11
|
+
CLOBBER.include "#{name}-*.gem"
|
10
12
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
s.version = version
|
15
|
-
s.homepage = "http://github.com/tuomas/tweetwine"
|
16
|
-
s.summary = "A simple Twitter agent for command line use"
|
17
|
-
s.description = "A simple but tasty Twitter agent for command line use, made for fun."
|
18
|
-
|
19
|
-
s.author = "Tuomas Kareinen"
|
20
|
-
s.email = "tkareine@gmail.com"
|
21
|
-
|
22
|
-
s.platform = Gem::Platform::RUBY
|
23
|
-
s.files = FileList[
|
24
|
-
"Rakefile",
|
25
|
-
"MIT-LICENSE.txt",
|
26
|
-
"*.rdoc",
|
27
|
-
"bin/**/*",
|
28
|
-
"contrib/**/*",
|
29
|
-
"example/**/*",
|
30
|
-
"lib/**/*",
|
31
|
-
"test/**/*"].to_a
|
32
|
-
s.executables = ["tweetwine"]
|
33
|
-
|
34
|
-
s.add_dependency("rest-client", ">= 1.0.0")
|
35
|
-
|
36
|
-
s.has_rdoc = true
|
37
|
-
s.extra_rdoc_files = FileList["MIT-LICENSE.txt", "*.rdoc"].to_a
|
38
|
-
s.rdoc_options << "--title" << "#{name} #{version}" \
|
39
|
-
<< "--main" << "README.rdoc" \
|
40
|
-
<< "--exclude" << "test" \
|
41
|
-
<< "--line-numbers"
|
42
|
-
end
|
13
|
+
file "#{name}.gem" => :"man:build" do |f|
|
14
|
+
sh %{gem build #{name}.gemspec}
|
15
|
+
end
|
43
16
|
|
44
|
-
|
45
|
-
|
46
|
-
pkg.need_tar = true
|
47
|
-
end
|
17
|
+
desc "Package the software as a gem"
|
18
|
+
task :build => [:"test:all", "#{name}.gem"]
|
48
19
|
|
49
|
-
desc "
|
50
|
-
task :
|
51
|
-
|
52
|
-
f.write spec.to_ruby
|
20
|
+
desc "Install the software as a gem"
|
21
|
+
task :install => :build do
|
22
|
+
sh %{gem install #{name}-#{version}.gem}
|
53
23
|
end
|
54
|
-
end
|
55
24
|
|
56
|
-
desc "
|
57
|
-
task :
|
58
|
-
|
25
|
+
desc "Uninstall the gem"
|
26
|
+
task :uninstall => :clean do
|
27
|
+
sh %{gem uninstall #{name}}
|
28
|
+
end
|
59
29
|
end
|
60
30
|
|
61
|
-
|
62
|
-
|
63
|
-
sh %{gem uninstall #{name}}
|
64
|
-
end
|
31
|
+
namespace :man do
|
32
|
+
CLOBBER.include "man/#{name}.?", "man/#{name}.?.html"
|
65
33
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
end
|
34
|
+
desc "Build the manual"
|
35
|
+
task :build do
|
36
|
+
sh "ronn -br5 --manual='#{name.capitalize} Manual' --organization='Tuomas Kareinen' man/*.ronn"
|
37
|
+
end
|
38
|
+
|
39
|
+
desc "Show the manual section 1"
|
40
|
+
task :show1 => :build do
|
41
|
+
sh "man man/#{name}.1"
|
42
|
+
end
|
75
43
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
t.verbose = true
|
81
|
-
t.warning = true
|
82
|
-
t.ruby_opts << "-rrubygems"
|
83
|
-
t.libs << "test"
|
44
|
+
desc "Show the manual section 7"
|
45
|
+
task :show7 => :build do
|
46
|
+
sh "man man/#{name}.7"
|
47
|
+
end
|
84
48
|
end
|
85
49
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
50
|
+
namespace :test do
|
51
|
+
require "rake/testtask"
|
52
|
+
|
53
|
+
desc "Run unit tests"
|
54
|
+
Rake::TestTask.new(:unit) do |t|
|
55
|
+
t.test_files = FileList["test/**/*_test.rb"]
|
56
|
+
t.verbose = true
|
57
|
+
t.warning = true
|
58
|
+
t.ruby_opts << "-rrubygems"
|
59
|
+
t.libs << "test"
|
60
|
+
end
|
61
|
+
|
62
|
+
desc "Run integration/example tests"
|
63
|
+
Rake::TestTask.new(:example) do |t|
|
64
|
+
t.test_files = FileList["example/**/*_example.rb"]
|
65
|
+
t.verbose = true
|
66
|
+
t.warning = false
|
67
|
+
t.ruby_opts << "-rrubygems"
|
68
|
+
t.libs << "example"
|
69
|
+
end
|
70
|
+
|
71
|
+
desc "Run all tests"
|
72
|
+
task :all => [:unit, :example]
|
92
73
|
end
|
93
74
|
|
94
75
|
desc "Find code smells"
|
@@ -96,9 +77,9 @@ task :roodi do
|
|
96
77
|
sh %{roodi "**/*.rb"}
|
97
78
|
end
|
98
79
|
|
99
|
-
desc "
|
80
|
+
desc "Show parts of the project tagged as incomplete"
|
100
81
|
task :todo do
|
101
|
-
FileList["
|
82
|
+
FileList["**/*.*"].egrep /(TODO|FIXME)/
|
102
83
|
end
|
103
84
|
|
104
|
-
task :default =>
|
85
|
+
task :default => :"test:all"
|
data/bin/tweetwine
CHANGED
data/example/example_helper.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
1
3
|
%w{
|
2
4
|
coulda
|
3
5
|
matchy
|
@@ -37,7 +39,7 @@ module Tweetwine
|
|
37
39
|
|
38
40
|
def fixture(filename)
|
39
41
|
contents = nil
|
40
|
-
filepath = File.dirname(__FILE__) << "/
|
42
|
+
filepath = File.dirname(__FILE__) << "/fixture/" << filename
|
41
43
|
File.open(filepath) do |f|
|
42
44
|
contents = f.readlines.join("\n")
|
43
45
|
end
|
data/lib/tweetwine/cli.rb
CHANGED
data/lib/tweetwine/client.rb
CHANGED
data/lib/tweetwine/io.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require "strscan"
|
1
4
|
require "uri"
|
2
5
|
|
3
6
|
module Tweetwine
|
@@ -92,11 +95,9 @@ module Tweetwine
|
|
92
95
|
def format_status(status)
|
93
96
|
status = Util.unescape_html(status)
|
94
97
|
if @colors
|
95
|
-
status =
|
96
|
-
status =
|
97
|
-
URI.extract(status, ["http", "https"]).uniq
|
98
|
-
status = colorize_all(:cyan, status, url)
|
99
|
-
end
|
98
|
+
status = colorize_matching(:yellow, status, USERNAME_REGEX)
|
99
|
+
status = colorize_matching(:magenta, status, HASHTAG_REGEX)
|
100
|
+
status = colorize_matching(:cyan, status, URI.extract(status, ["http", "https"]).uniq)
|
100
101
|
end
|
101
102
|
status
|
102
103
|
end
|
@@ -114,12 +115,16 @@ module Tweetwine
|
|
114
115
|
end
|
115
116
|
end
|
116
117
|
|
117
|
-
def
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
118
|
+
def colorize_matching(color, str, pattern)
|
119
|
+
regexp = case pattern
|
120
|
+
when Array
|
121
|
+
Regexp.union(pattern.map { |p| Regexp.new(Regexp.escape(p)) })
|
122
|
+
when Regexp
|
123
|
+
pattern
|
124
|
+
else
|
125
|
+
raise "Unknown kind of pattern"
|
126
|
+
end
|
127
|
+
Util.str_gsub_by_group(str, regexp) { |s| colorize(color, s) }
|
123
128
|
end
|
124
129
|
|
125
130
|
def colorize(color, str)
|
data/lib/tweetwine/meta.rb
CHANGED
data/lib/tweetwine/options.rb
CHANGED
data/lib/tweetwine/util.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
1
3
|
require "cgi"
|
2
4
|
require "time"
|
3
5
|
require "uri"
|
@@ -44,19 +46,20 @@ module Tweetwine
|
|
44
46
|
|
45
47
|
def self.str_gsub_by_group(str, regexp)
|
46
48
|
dup_str = str.dup
|
47
|
-
|
48
|
-
while
|
49
|
+
str_pos, dup_pos = 0, 0
|
50
|
+
while str_pos < str.size && (match_data = regexp.match(str[str_pos..-1]))
|
49
51
|
matching_group_indexes = indexes_of_filled_matches(match_data)
|
50
52
|
|
51
53
|
matching_group_indexes.each do |i|
|
52
54
|
replacement = (yield match_data[i]).to_s
|
53
|
-
dup_str[
|
55
|
+
dup_str[dup_pos + match_data.begin(i), match_data[i].size] = replacement
|
54
56
|
replacement_delta = replacement.size - match_data[i].size
|
55
|
-
|
57
|
+
dup_pos += replacement_delta
|
56
58
|
end
|
59
|
+
|
57
60
|
skip_delta = match_data.end(0)
|
58
|
-
|
59
|
-
|
61
|
+
str_pos += skip_delta
|
62
|
+
dup_pos += skip_delta
|
60
63
|
end
|
61
64
|
dup_str
|
62
65
|
end
|
data/man/tweetwine.1
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
.\" generated with Ronn/v0.5
|
2
|
+
.\" http://github.com/rtomayko/ronn/
|
3
|
+
.
|
4
|
+
.TH "TWEETWINE" "1" "April 2010" "Tuomas Kareinen" "Tweetwine Manual"
|
5
|
+
.
|
6
|
+
.SH "NAME"
|
7
|
+
\fBtweetwine\fR \-\- a simple Twitter command line agent
|
8
|
+
.
|
9
|
+
.SH "SYNOPSIS"
|
10
|
+
.
|
11
|
+
.nf
|
12
|
+
|
13
|
+
tweetwine [ <GLOBAL_OPTIONS> ]
|
14
|
+
tweetwine [ <GLOBAL_OPTIONS> ] <COMMAND> [ <COMMAND_OPTIONS> ]
|
15
|
+
.
|
16
|
+
.fi
|
17
|
+
.
|
18
|
+
.SH "DESCRIPTION"
|
19
|
+
Tweetwine supports showing the home timeline of the authenticated user, the
|
20
|
+
latest statuses of friends and followers, and the latest statuses that mention
|
21
|
+
the user. If that's not enough, statuses can be searched with arbitrary terms.
|
22
|
+
In addition, new statuses can be sent.
|
23
|
+
.
|
24
|
+
.P
|
25
|
+
\fICOMMAND\fR is one of
|
26
|
+
.
|
27
|
+
.IP "\(bu" 4
|
28
|
+
\fBfollowers\fR,
|
29
|
+
.
|
30
|
+
.IP "\(bu" 4
|
31
|
+
\fBfriends\fR,
|
32
|
+
.
|
33
|
+
.IP "\(bu" 4
|
34
|
+
\fBhome\fR,
|
35
|
+
.
|
36
|
+
.IP "\(bu" 4
|
37
|
+
\fBmentions\fR,
|
38
|
+
.
|
39
|
+
.IP "\(bu" 4
|
40
|
+
\fBsearch\fR,
|
41
|
+
.
|
42
|
+
.IP "\(bu" 4
|
43
|
+
\fBupdate\fR, or
|
44
|
+
.
|
45
|
+
.IP "\(bu" 4
|
46
|
+
\fBuser\fR.
|
47
|
+
.
|
48
|
+
.IP "" 0
|
49
|
+
.
|
50
|
+
.P
|
51
|
+
The default is \fBhome\fR.
|
52
|
+
.
|
53
|
+
.SH "OPTIONS"
|
54
|
+
Options given from command line override corresponding settings from
|
55
|
+
configuration (\fB~/.tweetwine\fR).
|
56
|
+
.
|
57
|
+
.P
|
58
|
+
\fIGLOBAL_OPTIONS\fR are:
|
59
|
+
.
|
60
|
+
.TP
|
61
|
+
\fB\-a\fR, \fB\-\-auth USERNAME:PASSWORD\fR
|
62
|
+
Authentication.
|
63
|
+
.
|
64
|
+
.TP
|
65
|
+
\fB\-c\fR, \fB\-\-[no\-]colors\fR
|
66
|
+
Colorize output with ANSI escape codes.
|
67
|
+
.
|
68
|
+
.TP
|
69
|
+
\fB\-n\fR, \fB\-\-num N\fR
|
70
|
+
The number of statuses in page, default 20.
|
71
|
+
.
|
72
|
+
.TP
|
73
|
+
\fB\-\-[no\-]http\-proxy URL\fR
|
74
|
+
Use proxy for HTTP and HTTPS.
|
75
|
+
.
|
76
|
+
.TP
|
77
|
+
\fB\-\-no\-url\-shorten\fR
|
78
|
+
Do not shorten URLs for status update.
|
79
|
+
.
|
80
|
+
.TP
|
81
|
+
\fB\-p\fR, \fB\-\-page N\fR
|
82
|
+
The page number for statuses, default 1.
|
83
|
+
.
|
84
|
+
.TP
|
85
|
+
\fB\-v\fR, \fB\-\-version\fR
|
86
|
+
Show version information and exit.
|
87
|
+
.
|
88
|
+
.TP
|
89
|
+
\fB\-h\fR, \fB\-\-help\fR
|
90
|
+
Show help message and exit.
|
91
|
+
.
|
92
|
+
.P
|
93
|
+
In order to see \fICOMMAND_OPTIONS\fR, enter:
|
94
|
+
.
|
95
|
+
.IP "" 4
|
96
|
+
.
|
97
|
+
.nf
|
98
|
+
|
99
|
+
$ tweetwine help <COMMAND>
|
100
|
+
.
|
101
|
+
.fi
|
102
|
+
.
|
103
|
+
.IP "" 0
|
104
|
+
.
|
105
|
+
.SH "COPYRIGHT"
|
106
|
+
Tweetwine is Copyright (c) 2009\-2010 Tuomas Kareinen
|
107
|
+
.
|
108
|
+
.SH "SEE ALSO"
|
109
|
+
tweetwine(7), \fIhttp://github.com/tuomas/tweetwine\fR
|