fry-send_tweet.rb 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|