fry-send_tweet.rb 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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +282 -0
- data/LICENSE.txt +24 -0
- data/PRY_LICENSE.txt +25 -0
- data/README.md +456 -0
- data/Vagrantfile +46 -0
- data/art/44-e44743a5bb.jpg +0 -0
- data/art/52-eec4df2edf.jpg +0 -0
- data/lib/pry-send_tweet.rb +75 -0
- data/lib/pry/pager/system_pager.rb +25 -0
- data/lib/pry/send_tweet/commands/base_command.rb +70 -0
- data/lib/pry/send_tweet/commands/easter_eggs.rb +184 -0
- data/lib/pry/send_tweet/commands/paging/paging_support.rb +16 -0
- data/lib/pry/send_tweet/commands/read_tweets.rb +93 -0
- data/lib/pry/send_tweet/commands/read_tweets/translate_actions.rb +94 -0
- data/lib/pry/send_tweet/commands/send_tweet.rb +151 -0
- data/lib/pry/send_tweet/commands/twitter_action.rb +118 -0
- data/lib/pry/send_tweet/commands/twitter_action/delete_tweet_actions.rb +19 -0
- data/lib/pry/send_tweet/commands/twitter_action/follow_actions.rb +66 -0
- data/lib/pry/send_tweet/commands/twitter_action/like_actions.rb +23 -0
- data/lib/pry/send_tweet/commands/twitter_action/mute_actions.rb +23 -0
- data/lib/pry/send_tweet/commands/twitter_action/profile_actions.rb +60 -0
- data/lib/pry/send_tweet/commands/twitter_action/suggested_actions.rb +20 -0
- data/lib/pry/send_tweet/commands/twitter_search.rb +36 -0
- data/lib/pry/send_tweet/renderers/tweet_renderer.rb +103 -0
- data/lib/pry/send_tweet/renderers/user_renderer.rb +17 -0
- data/lib/pry/send_tweet/tty-box.rb +73 -0
- data/lib/pry/send_tweet/twitter_io.rb +26 -0
- data/lib/pry/send_tweet/version.rb +5 -0
- data/lib/time-ago-in-words/README.md +14 -0
- data/lib/time-ago-in-words/lib/time-ago-in-words.rb +37 -0
- data/lib/time-ago-in-words/time-ago-in-words.gemspec +14 -0
- data/pry-send_tweet.gemspec +23 -0
- data/samples/freebsd-zshrc +5 -0
- data/samples/tmuxinator-vagrant.yml +11 -0
- data/vms/freebsd.rb +15 -0
- metadata +123 -0
@@ -0,0 +1,94 @@
|
|
1
|
+
module Pry::SendTweet::ReadTweets::TranslateActions
|
2
|
+
YANDEX_ENDPOINT = "https://translate.yandex.net/api/v1.5/tr.json/translate?" \
|
3
|
+
"key=%{key}&text=%{text}&lang=%{lang}"
|
4
|
+
|
5
|
+
LANGUAGE_STRINGS = {
|
6
|
+
'ar' => 'Arabic (العربية)',
|
7
|
+
'en' => 'English',
|
8
|
+
'de' => 'German (Deutsch)',
|
9
|
+
'pt' => 'Portuguese (Portuguesa)',
|
10
|
+
'fa' => 'Farsi (دریافت)',
|
11
|
+
'ja' => 'Japanese (日本語)',
|
12
|
+
'he' => 'Hebrew (עברית)',
|
13
|
+
'ga' => 'Irish (Gaeilge)',
|
14
|
+
'es' => 'Spanish (Español)',
|
15
|
+
'it' => 'Italinao (italiano)',
|
16
|
+
'nl' => 'Dutch (Nederlands)',
|
17
|
+
'ru' => 'Russian (русский)',
|
18
|
+
'uk' => 'Ukranian (країнська)',
|
19
|
+
'ko' => 'Korean (한국어)',
|
20
|
+
'fr' => 'French (Français)',
|
21
|
+
'da' => 'Danish (dansk)',
|
22
|
+
'yi' => 'Yiddish (ייִדיש)',
|
23
|
+
'sw' => 'Swahili'
|
24
|
+
}
|
25
|
+
|
26
|
+
def translate_tweet(tweet_url, source_language)
|
27
|
+
tweet = twitter.status(tweet_url)
|
28
|
+
uri_endpoint = __build_yandex_endpoint(tweet, source_language)
|
29
|
+
res = Net::HTTP.get_response(uri_endpoint)
|
30
|
+
case res
|
31
|
+
when Net::HTTPOK
|
32
|
+
tweet.attrs[:full_text] = __translated_text_from(res)
|
33
|
+
render_tweets [tweet], title: "#{__translation_map(res)}: "
|
34
|
+
else
|
35
|
+
raise Pry::CommandError, "Bad response from Yandex (#{res.class})"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def translate_text(text, source_language)
|
40
|
+
uri_endpoint = __build_yandex_endpoint(text, source_language)
|
41
|
+
res = Net::HTTP.get_response(uri_endpoint)
|
42
|
+
case res
|
43
|
+
when Net::HTTPOK
|
44
|
+
_pry_.output.puts "#{__translation_map(res)}: \n" \
|
45
|
+
"#{__translated_text_from(res)}"
|
46
|
+
else
|
47
|
+
raise Pry::CommandError, "Bad response from Yandex (#{res.class})"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
# @api private
|
54
|
+
def __translation_map(res)
|
55
|
+
b = JSON.parse(res.body)
|
56
|
+
from, to = b['lang'].split('-')
|
57
|
+
from = LANGUAGE_STRINGS[from] || from
|
58
|
+
to = LANGUAGE_STRINGS[to] || to
|
59
|
+
"#{from} => #{to}"
|
60
|
+
end
|
61
|
+
|
62
|
+
# @api private
|
63
|
+
def __translated_text_from(res)
|
64
|
+
JSON.parse(res.body)["text"][0]
|
65
|
+
end
|
66
|
+
|
67
|
+
# @api private
|
68
|
+
def __build_yandex_endpoint(tweet, source_language)
|
69
|
+
URI.parse format(YANDEX_ENDPOINT,
|
70
|
+
lang: URI.encode_www_form_component(__yandex_lang_param(source_language)),
|
71
|
+
key: URI.encode_www_form_component(__yandex_key),
|
72
|
+
text: URI.encode_www_form_component(
|
73
|
+
Twitter::Tweet === tweet ? __read_tweet_body(tweet) : tweet
|
74
|
+
)
|
75
|
+
)
|
76
|
+
end
|
77
|
+
|
78
|
+
# @api private
|
79
|
+
def __yandex_lang_param(source_language)
|
80
|
+
from = source_language ? "#{source_language}-" : ""
|
81
|
+
to = _pry_.config.twitter.yandex_lang || "en"
|
82
|
+
from + to
|
83
|
+
end
|
84
|
+
|
85
|
+
# @api private
|
86
|
+
def __yandex_key
|
87
|
+
k = _pry_.config.twitter.yandex_key
|
88
|
+
if !k || k.strip.empty?
|
89
|
+
raise Pry::CommandError,
|
90
|
+
"fatal: _pry_.config.twitter.yandex_key is nil, false or empty"
|
91
|
+
end
|
92
|
+
k
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
class Pry::SendTweet::SendTweet < Pry::SendTweet::BaseCommand
|
2
|
+
MAX_TWEET_SIZE = 280
|
3
|
+
|
4
|
+
match 'send-tweet'
|
5
|
+
description 'Send a tweet.'
|
6
|
+
command_options argument_required: false
|
7
|
+
banner <<-BANNER
|
8
|
+
send-tweet [options]
|
9
|
+
|
10
|
+
Send a tweet.
|
11
|
+
BANNER
|
12
|
+
|
13
|
+
def options(o)
|
14
|
+
o.on 'f=', 'file=',
|
15
|
+
'One or more paths to file(s) to attach to a tweet.',
|
16
|
+
as: Array
|
17
|
+
o.on 'r=', 'reply-to=',
|
18
|
+
'An absolute url to a tweet you want to reply to.'
|
19
|
+
o.on 's=', 'self-destruct=',
|
20
|
+
'The number of seconds (represented as a number or a timestamp in the ' \
|
21
|
+
'format of HH:MM:SS) to wait before automatically deleting the tweet ' \
|
22
|
+
'asynchronously.'
|
23
|
+
o.on 'd=', 'delay=',
|
24
|
+
'The number of seconds (represented as a number or a timestamp in the ' \
|
25
|
+
'format of HH:MM:SS) to wait before creating the tweet asynchronously.'
|
26
|
+
o.on 'n', 'no-newline',
|
27
|
+
"Remove newlines (\\n) from a tweet before sending it."
|
28
|
+
end
|
29
|
+
|
30
|
+
def process(args)
|
31
|
+
super
|
32
|
+
tweet_creator = create_tweet_creator(compose_tweet_with_editor)
|
33
|
+
if opts.present?('delay')
|
34
|
+
time_obj, sleep_seconds = parse_duration_str(opts['delay'])
|
35
|
+
Thread.new {
|
36
|
+
sleep sleep_seconds
|
37
|
+
tweet_creator.call
|
38
|
+
}
|
39
|
+
publish_time = (time_obj ? time_obj : Time.now + sleep_seconds)
|
40
|
+
.getlocal
|
41
|
+
.strftime(time_format)
|
42
|
+
page_ok bold("Tweet will be published at approximately #{publish_time}.")
|
43
|
+
else
|
44
|
+
tweet = tweet_creator.call
|
45
|
+
page_ok tweet.url
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def paths_to_twitterio(paths)
|
52
|
+
paths.map do |path|
|
53
|
+
Pry::SendTweet::TwitterIO.new File.binread(path), File.basename(path)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def no_newline?
|
58
|
+
opts.present?('no-newline')
|
59
|
+
end
|
60
|
+
|
61
|
+
def send_tweet_with_media(tweet_contents, medias)
|
62
|
+
tweet = twitter.update_with_media(tweet_contents, medias, tweet_options)
|
63
|
+
tweet.tap {|t| self_destruct! t.id, opts['self-destruct'] }
|
64
|
+
ensure
|
65
|
+
medias.each(&:close)
|
66
|
+
end
|
67
|
+
|
68
|
+
def send_textual_tweet(tweet_contents)
|
69
|
+
tweet = twitter.update(tweet_contents, tweet_options)
|
70
|
+
tweet.tap {|t| self_destruct! t.id, opts['self-destruct'] }
|
71
|
+
end
|
72
|
+
|
73
|
+
def prepend_username_to_reply!(tweet_url, file)
|
74
|
+
tweet = twitter.status(tweet_url)
|
75
|
+
mentions = [tweet.user.screen_name].concat tweet.user_mentions.map(&:screen_name)
|
76
|
+
file.write mentions.uniq(&:downcase).map{|u| "@#{u}" }.join(' ')
|
77
|
+
end
|
78
|
+
|
79
|
+
def replying_to_other_tweet?
|
80
|
+
opts.present?('reply-to')
|
81
|
+
end
|
82
|
+
|
83
|
+
def tweet_options
|
84
|
+
options = {}
|
85
|
+
options.merge!({
|
86
|
+
in_reply_to_status_id: Integer(File.basename(opts['reply-to']))
|
87
|
+
}) if replying_to_other_tweet?
|
88
|
+
options
|
89
|
+
end
|
90
|
+
|
91
|
+
def compose_tweet_with_editor
|
92
|
+
tweet = Tempfile.open('pry-send_tweet--compose-tweet') do |file|
|
93
|
+
if replying_to_other_tweet?
|
94
|
+
# During experimentation I noticed that without prefixing who we are
|
95
|
+
# replying to, the tweet we send will not be considered a reply even
|
96
|
+
# with reply_to_status_id being set.
|
97
|
+
prepend_username_to_reply!(opts['reply-to'], file)
|
98
|
+
end
|
99
|
+
file.rewind
|
100
|
+
Pry::Editor.new(_pry_).invoke_editor(file.path, 0)
|
101
|
+
lines = file.read.each_line
|
102
|
+
no_newline? ? lines.map(&:chomp).join(' ') : lines.to_a.join
|
103
|
+
end
|
104
|
+
validate_tweet!(tweet)
|
105
|
+
tweet
|
106
|
+
end
|
107
|
+
|
108
|
+
def create_tweet_creator(tweet_contents)
|
109
|
+
if opts.present?(:file)
|
110
|
+
medias = paths_to_twitterio(opts[:file])
|
111
|
+
lambda { send_tweet_with_media(tweet_contents, medias) }
|
112
|
+
else
|
113
|
+
lambda { send_textual_tweet(tweet_contents) }
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def validate_tweet!(tweet)
|
118
|
+
if tweet.strip.empty?
|
119
|
+
raise Pry::CommandError, "Can't post an empty tweet."
|
120
|
+
elsif tweet.size > MAX_TWEET_SIZE
|
121
|
+
raise Pry::CommandError, "The tweet: \n" +
|
122
|
+
word_wrap(tweet) +
|
123
|
+
"\nis too big to publish, try to use less characters."
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def self_destruct!(tweet_id, duration)
|
128
|
+
return if !duration
|
129
|
+
_, sleep_seconds = parse_duration_str(duration)
|
130
|
+
page bold("Tweet due to self destruct in #{sleep_seconds} seconds")
|
131
|
+
Thread.new do
|
132
|
+
sleep sleep_seconds
|
133
|
+
twitter.destroy_status(tweet_id)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def parse_duration_str(str)
|
138
|
+
if str =~ /\A\d+\z/
|
139
|
+
sleep_seconds = Integer(str)
|
140
|
+
elsif str =~ /\A\d{2}:\d{2}\z/ || str =~ /\A\d{2}:\d{2}:\d{2}\z/
|
141
|
+
time_obj = Time.parse(str)
|
142
|
+
time_obj += 3600*24 if time_obj <= Time.now
|
143
|
+
sleep_seconds = Integer(time_obj - Time.now)
|
144
|
+
else
|
145
|
+
raise Pry::CommandError, "--delay='#{str}' or --self-destruct='#{str}' is not " \
|
146
|
+
"something I understand."
|
147
|
+
end
|
148
|
+
[time_obj, sleep_seconds]
|
149
|
+
end
|
150
|
+
Pry.commands.add_command self
|
151
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
|
2
|
+
class Pry::SendTweet::TwitterAction < Pry::SendTweet::BaseCommand
|
3
|
+
require_relative 'twitter_action/profile_actions'
|
4
|
+
require_relative 'twitter_action/suggested_actions'
|
5
|
+
require_relative 'twitter_action/like_actions'
|
6
|
+
require_relative 'twitter_action/follow_actions'
|
7
|
+
require_relative 'twitter_action/mute_actions'
|
8
|
+
require_relative 'twitter_action/delete_tweet_actions'
|
9
|
+
include ProfileActions
|
10
|
+
include SuggestedActions
|
11
|
+
include LikeActions
|
12
|
+
include FollowActions
|
13
|
+
include MuteActions
|
14
|
+
include DeleteTweetActions
|
15
|
+
|
16
|
+
match 'twitter-action'
|
17
|
+
description 'Like, unlike, follow, unfollow and more.'
|
18
|
+
banner <<-B
|
19
|
+
twitter-action OPTIONS
|
20
|
+
|
21
|
+
#{description}
|
22
|
+
B
|
23
|
+
|
24
|
+
def options(o)
|
25
|
+
#
|
26
|
+
# Like / unlike tweets
|
27
|
+
#
|
28
|
+
o.on 'like-tweet=', 'Like one or more tweets', as: Array
|
29
|
+
o.on 'unlike-tweet=', 'Unlike one or more tweets', as: Array
|
30
|
+
#
|
31
|
+
# Following / unfollowing
|
32
|
+
#
|
33
|
+
o.on 'follow=', 'Follow one or more users', as: Array
|
34
|
+
o.on 'unfollow=', 'Unfollow one or more users', as: Array
|
35
|
+
o.on 'show-followers=', 'Show the newest users to have followed you or ' \
|
36
|
+
'another user', argument: :optional
|
37
|
+
o.on 'show-following', 'Show the newest users you currently follow'
|
38
|
+
#
|
39
|
+
# Delete tweets and reweets
|
40
|
+
#
|
41
|
+
o.on 'delete-retweet=', 'Delete one or more retweets', as: Array
|
42
|
+
o.on 'delete-tweet=', 'Delete one or more tweets', as: Array
|
43
|
+
#
|
44
|
+
# Retweet a tweet
|
45
|
+
#
|
46
|
+
o.on 'retweet=', 'Retweet one or more tweets', as: Array
|
47
|
+
#
|
48
|
+
# Profile management
|
49
|
+
#
|
50
|
+
o.on 'set-profile-name=', 'Set the name visible on your profile'
|
51
|
+
o.on 'set-profile-bio', 'Set the description visible on your profile'
|
52
|
+
o.on 'set-profile-location', 'Set the location visible on your profile'
|
53
|
+
o.on 'set-profile-url=', 'Set the URL visible on your profile'
|
54
|
+
o.on 'set-profile-banner-image=', 'Set the banner image visible on your profile'
|
55
|
+
o.on 'set-profile-image=', 'Set profile photo'
|
56
|
+
o.on 'set-profile-link-color=', 'Set the color (as a hex value) of links that ' \
|
57
|
+
'are visible on your profiles timeline '
|
58
|
+
#
|
59
|
+
# Suggestions
|
60
|
+
#
|
61
|
+
o.on 'suggested-topics', 'View topics suggested by Twitter'
|
62
|
+
o.on 'suggested-users=', 'A topic from which to view users Twitter ' \
|
63
|
+
'suggests following'
|
64
|
+
o.on 'suggested-lang=', 'Restrict suggestion results to a specific language, ' \
|
65
|
+
'given in ISO 639-1 format. Default is "en"'
|
66
|
+
#
|
67
|
+
# Muting
|
68
|
+
#
|
69
|
+
o.on 'mute-user=', 'One or more usernames to mute', as: Array
|
70
|
+
o.on 'unmute-user=', 'One or more usernames to unmute', as: Array
|
71
|
+
end
|
72
|
+
|
73
|
+
def process
|
74
|
+
super
|
75
|
+
case
|
76
|
+
when opts.present?('like-tweet') then like_tweet(opts['like-tweet'])
|
77
|
+
when opts.present?('unlike-tweet') then unlike_tweet(opts['unlike-tweet'])
|
78
|
+
when opts.present?('follow') then follow_user(opts['follow'])
|
79
|
+
when opts.present?('unfollow') then unfollow_user(opts['unfollow'])
|
80
|
+
when opts.present?('delete-tweet') then delete_tweet(opts['delete-tweet'])
|
81
|
+
when opts.present?('delete-retweet') then delete_retweet(opts['delete-retweet'])
|
82
|
+
when opts.present?('retweet') then retweet_tweet(opts['retweet'])
|
83
|
+
when opts.present?('set-profile-banner-image') then set_profile_banner_image(opts['set-profile-banner-image'])
|
84
|
+
when opts.present?('set-profile-name') then set_profile_name(opts['set-profile-name'])
|
85
|
+
when opts.present?('set-profile-image') then set_profile_image(opts['set-profile-image'])
|
86
|
+
when opts.present?('set-profile-url') then set_profile_url(opts['set-profile-url'])
|
87
|
+
when opts.present?('set-profile-location') then set_profile_location
|
88
|
+
when opts.present?('set-profile-link-color') then set_profile_link_color(opts['set-profile-link-color'])
|
89
|
+
when opts.present?('set-profile-bio') then set_profile_bio
|
90
|
+
when opts.present?('suggested-topics') then suggested_topics
|
91
|
+
when opts.present?('suggested-users') then suggested_users(opts['suggested-users'])
|
92
|
+
when opts.present?('mute-user') then mute_user(opts['mute-user'])
|
93
|
+
when opts.present?('unmute-user') then unmute_user(opts['unmute-user'])
|
94
|
+
when opts.present?('show-followers') then show_followers(opts['show-followers'])
|
95
|
+
when opts.present?('show-following') then show_following(opts['show-following'])
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
def retweet_tweet(tweets)
|
102
|
+
retweets = twitter.retweet(tweets)
|
103
|
+
if retweets.size > 0
|
104
|
+
render_tweets retweets, title: bright_green("Retweeted tweets"), timeout: false
|
105
|
+
else
|
106
|
+
page_error word_wrap("Nothing retweeted. Are you sure you gave a valid reference to " \
|
107
|
+
"one or more tweets, and that you haven't already retweeted the given " \
|
108
|
+
"tweet(s) ..?")
|
109
|
+
end
|
110
|
+
rescue Twitter::Error => e
|
111
|
+
page_error word_wrap("Nothing retweeted. Are you sure you gave a valid reference to " \
|
112
|
+
"one or more tweets, and that you haven't already retweeted the given " \
|
113
|
+
"tweet(s) ..?")
|
114
|
+
end
|
115
|
+
|
116
|
+
Pry.commands.add_command(self)
|
117
|
+
Pry.commands.alias_command "on-twitter", "twitter-action"
|
118
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Pry::SendTweet::TwitterAction::DeleteTweetActions
|
2
|
+
def delete_retweet(retweets)
|
3
|
+
tweets = twitter.unretweet(retweets)
|
4
|
+
if tweets.size == 0
|
5
|
+
page_error "Are you sure you gave a valid reference to one or more retweets?"
|
6
|
+
else
|
7
|
+
render_tweets tweets, title: bold("Deleted retweets"), timeout: false
|
8
|
+
end
|
9
|
+
rescue Twitter::Error => e
|
10
|
+
page_error(e)
|
11
|
+
end
|
12
|
+
|
13
|
+
def delete_tweet(tweets)
|
14
|
+
tweets = twitter.destroy_status(tweets)
|
15
|
+
render_tweets tweets, title: bold("Deleted tweets"), timeout: false
|
16
|
+
rescue Twitter::Error => e
|
17
|
+
page_error(e)
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module Pry::SendTweet::TwitterAction::FollowActions
|
2
|
+
def follow_user(users)
|
3
|
+
users = search_str_for_users(*users)
|
4
|
+
follows = twitter.follow(users)
|
5
|
+
if follows.size > 0
|
6
|
+
page_ok "followed #{join_usernames(follows.map(&:screen_name))}."
|
7
|
+
else
|
8
|
+
page_error "are you already following #{join_usernames(users)} ..?"
|
9
|
+
end
|
10
|
+
rescue Twitter::Error => e
|
11
|
+
page_error(e)
|
12
|
+
end
|
13
|
+
|
14
|
+
def unfollow_user(users)
|
15
|
+
users = search_str_for_users(*users)
|
16
|
+
unfollows = twitter.unfollow(users)
|
17
|
+
if unfollows.size > 0
|
18
|
+
page_ok "unfollowed #{join_usernames(unfollows.map(&:screen_name))}."
|
19
|
+
else
|
20
|
+
page_error "did you already unfollow #{join_usernames(users)} ..?"
|
21
|
+
end
|
22
|
+
rescue Twitter::Error => e
|
23
|
+
page_error(e)
|
24
|
+
end
|
25
|
+
|
26
|
+
def show_followers(pattern)
|
27
|
+
followers = Array twitter.followers(follow_request_options)
|
28
|
+
__follow_filter!(followers, pattern) if pattern
|
29
|
+
page numbered_list("Followers", followers) {|follower, index|
|
30
|
+
render_user(follower)
|
31
|
+
}
|
32
|
+
rescue Twitter::Error => e
|
33
|
+
page_error(e)
|
34
|
+
end
|
35
|
+
|
36
|
+
def show_following(pattern)
|
37
|
+
followings = Array twitter.following(follow_request_options)
|
38
|
+
__follow_filter!(followings, pattern) if pattern
|
39
|
+
page numbered_list("Following", followings) {|following, index|
|
40
|
+
render_user(following)
|
41
|
+
}
|
42
|
+
rescue Twitter::Error => e
|
43
|
+
page_error(e)
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
# @api private
|
49
|
+
def __follow_filter!(users, pattern)
|
50
|
+
users.select! do |u|
|
51
|
+
u.screen_name =~ /#{pattern}/
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# @api private
|
56
|
+
def follow_request_options
|
57
|
+
{skip_status: true, include_user_entities: true}
|
58
|
+
end
|
59
|
+
|
60
|
+
# @api private
|
61
|
+
def join_usernames(users)
|
62
|
+
users.map do |username|
|
63
|
+
bold("@#{username}") + " ( https://twitter.com/#{username} )"
|
64
|
+
end.join(', ')
|
65
|
+
end
|
66
|
+
end
|