pry-twitter 1.22.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 +13 -0
- data/LICENSE.txt +24 -0
- data/PRY_LICENSE.txt +25 -0
- data/README.md +546 -0
- data/Vagrantfile +48 -0
- data/art/16-60174b5812.jpg +0 -0
- data/art/16-a31deea6f0.jpg +0 -0
- data/art/17-b3d5d6f6c2.jpg +0 -0
- data/art/44-e44743a5bb.jpg +0 -0
- data/art/51-5e4dc5fd92.jpg +0 -0
- data/art/51-82bde81305.jpg +0 -0
- data/art/51-ad821fa377.jpg +0 -0
- data/art/51-b90d5dd6a6.jpg +0 -0
- data/art/51-da43a4ef4f.jpg +0 -0
- data/art/51-e94c3387e1.jpg +0 -0
- data/art/52-eec4df2edf.jpg +0 -0
- data/art/53-9d231893e1.jpg +0 -0
- data/art/54-39b1063483.jpg +0 -0
- data/art/55-e41f9896d0.jpg +0 -0
- data/art/56-c82d4e5b61.jpg +0 -0
- data/art/59-2a2c9cd962.jpg +0 -0
- data/art/61-3970c79d47.jpg +0 -0
- data/art/62-5704cc1652.jpg +0 -0
- data/art/62-b0aceb0fa6.jpg +0 -0
- data/art/62-da0faf972e.jpg +0 -0
- data/art/frankewilde.jpg +0 -0
- data/art/rollingstone.jpg +0 -0
- data/art/tide.jpg +0 -0
- data/art/windows10.png +0 -0
- data/lib/pry-send_tweet.rb +77 -0
- data/lib/pry/pager/system_pager.rb +25 -0
- data/lib/pry/send_tweet/commands/base_command.rb +72 -0
- data/lib/pry/send_tweet/commands/easter_eggs.rb +564 -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 +164 -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 +6 -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/loader.conf +1 -0
- data/samples/tmuxinator-vagrant.yml +11 -0
- data/vms/freebsd.rb +18 -0
- metadata +145 -0
@@ -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
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Pry::SendTweet::TwitterAction::LikeActions
|
2
|
+
def like_tweet(tweets)
|
3
|
+
liked = twitter.favorite(tweets)
|
4
|
+
if liked.size > 0
|
5
|
+
render_tweets liked, title: bold("Liked tweets"), timeout: false
|
6
|
+
else
|
7
|
+
page_error "No tweets liked, are you sure you didn't already like those tweet(s) ..?"
|
8
|
+
end
|
9
|
+
rescue Twitter::Error => e
|
10
|
+
page_error(e)
|
11
|
+
end
|
12
|
+
|
13
|
+
def unlike_tweet(tweets)
|
14
|
+
unliked = twitter.unfavorite(tweets)
|
15
|
+
if unliked.size > 0
|
16
|
+
render_tweets unliked, title: bold("Unliked tweets"), timeout: false
|
17
|
+
else
|
18
|
+
page_error "No tweets unliked, are you sure you had liked those tweet(s) ..?"
|
19
|
+
end
|
20
|
+
rescue Twitter::Error => e
|
21
|
+
page_error(e)
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Pry::SendTweet::TwitterAction::MuteActions
|
2
|
+
def mute_user(strings)
|
3
|
+
muted = twitter.mute(*search_str_for_users(*strings))
|
4
|
+
if muted.size > 0
|
5
|
+
page numbered_list("Muted users", muted) {|user, index|
|
6
|
+
render_user(user)
|
7
|
+
}
|
8
|
+
else
|
9
|
+
page_error "No one was muted, did you mute those user(s) already ..?"
|
10
|
+
end
|
11
|
+
rescue Twitter::Error => e
|
12
|
+
page_error(e)
|
13
|
+
end
|
14
|
+
|
15
|
+
def unmute_user(strings)
|
16
|
+
unmuted = twitter.unmute(*search_str_for_users(*strings))
|
17
|
+
page numbered_list("Unmuted users", unmuted) {|user, index|
|
18
|
+
render_user(user)
|
19
|
+
}
|
20
|
+
rescue Twitter::Error => e
|
21
|
+
page_error(e)
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Pry::SendTweet::TwitterAction::ProfileActions
|
2
|
+
def set_profile_url(url)
|
3
|
+
if twitter.update_profile url: url
|
4
|
+
page_ok "URL updated"
|
5
|
+
else
|
6
|
+
raise Pry::CommandError, "Something went wrong"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def set_profile_image(path)
|
11
|
+
twitter.update_profile_image(path)
|
12
|
+
page_ok "Profile image updated"
|
13
|
+
rescue Twitter::Error => e
|
14
|
+
page_error(e)
|
15
|
+
end
|
16
|
+
|
17
|
+
def set_profile_banner_image(path)
|
18
|
+
base64 = Base64.strict_encode64 File.binread(path)
|
19
|
+
twitter.update_profile_banner(base64)
|
20
|
+
page_ok "Profile banner updated"
|
21
|
+
rescue Twitter::Error => e
|
22
|
+
page_error(e)
|
23
|
+
end
|
24
|
+
|
25
|
+
def set_profile_name(name)
|
26
|
+
twitter.update_profile name: name
|
27
|
+
page_ok "Name updated"
|
28
|
+
rescue Twitter::Error => e
|
29
|
+
page_error(e)
|
30
|
+
end
|
31
|
+
|
32
|
+
def set_profile_bio
|
33
|
+
Tempfile.open('pry-send_tweet-set-profile-bio') do |file|
|
34
|
+
Pry::Editor.new(_pry_).invoke_editor(file.path, 0)
|
35
|
+
file.rewind
|
36
|
+
twitter.update_profile description: file.read
|
37
|
+
page_ok "Bio updated"
|
38
|
+
end
|
39
|
+
rescue Twitter::Error => e
|
40
|
+
page_error(e)
|
41
|
+
end
|
42
|
+
|
43
|
+
def set_profile_location
|
44
|
+
Tempfile.open('pry-send_tweet-set-profile-location') do |file|
|
45
|
+
Pry::Editor.new(_pry_).invoke_editor(file.path, 0)
|
46
|
+
file.rewind
|
47
|
+
twitter.update_profile location: file.read
|
48
|
+
page_ok "Location updated"
|
49
|
+
end
|
50
|
+
rescue Twitter::Error => e
|
51
|
+
page_error e
|
52
|
+
end
|
53
|
+
|
54
|
+
def set_profile_link_color(color)
|
55
|
+
twitter.update_profile profile_link_color: color
|
56
|
+
page_ok "Profile link color updated"
|
57
|
+
rescue Twitter::Error => e
|
58
|
+
page_error e
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Pry::SendTweet::TwitterAction::SuggestedActions
|
2
|
+
def suggested_topics
|
3
|
+
suggested_topics = twitter.suggestions lang: suggested_lang
|
4
|
+
page numbered_list("Suggested topics: ", suggested_topics) {|topic, index|
|
5
|
+
"#{index}. #{topic.slug}"
|
6
|
+
}
|
7
|
+
end
|
8
|
+
|
9
|
+
def suggested_users(topic)
|
10
|
+
topic = URI.escape(topic)
|
11
|
+
users = twitter.suggestions(topic, lang: suggested_lang).users
|
12
|
+
page numbered_list("Suggested users: ", users) {|user, index|
|
13
|
+
render_user(user)
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
def suggested_lang
|
18
|
+
opts['suggested-lang'] || 'en'
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
class Pry::SendTweet::TwitterSearch < Pry::SendTweet::BaseCommand
|
2
|
+
match 'twitter-search'
|
3
|
+
description 'Search Twitter.'
|
4
|
+
command_options argument_required: true, shellwords: false
|
5
|
+
banner <<-BANNER
|
6
|
+
twitter-search [options] query
|
7
|
+
|
8
|
+
#{description}
|
9
|
+
BANNER
|
10
|
+
|
11
|
+
def options(o)
|
12
|
+
o.on 'c=', 'count=', "The number of tweets to show. Default is " \
|
13
|
+
"#{default_read_size}."
|
14
|
+
end
|
15
|
+
|
16
|
+
def process(query)
|
17
|
+
super
|
18
|
+
tweets = filter twitter.search(query, search_options).to_a
|
19
|
+
if tweets.empty?
|
20
|
+
page "No results to show."
|
21
|
+
else
|
22
|
+
q = bright_blue(bold(query))
|
23
|
+
render_tweets(tweets, title: format("Showing search results for %{q}", q: q))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private def filter(tweets)
|
28
|
+
tweets.reject(&:retweet?)
|
29
|
+
end
|
30
|
+
|
31
|
+
private def search_options
|
32
|
+
{count: opts['count'] || default_read_size, tweet_mode: 'extended'}
|
33
|
+
end
|
34
|
+
|
35
|
+
Pry.commands.add_command self
|
36
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module Pry::SendTweet::TweetRenderer
|
2
|
+
include Timeout
|
3
|
+
include TimeAgoInWords
|
4
|
+
|
5
|
+
def render_tweets(tweet_fetcher, title: nil, timeout: _pry_.config.twitter.refresh_interval)
|
6
|
+
pager = Pry::Pager::SystemPager.new(_pry_.output).tap(&:fork)
|
7
|
+
interval = __choose_render_interval(timeout)
|
8
|
+
timeout(interval) do
|
9
|
+
began_at, refresh_at = __find_timeout_range(interval)
|
10
|
+
rendered_title = __choose_title(title, began_at, refresh_at)
|
11
|
+
tweets = __fetch_tweets(tweet_fetcher)
|
12
|
+
tweets.empty? ? pager.write("No tweets to show.") :
|
13
|
+
pager.write(__render_tweets(rendered_title, tweets))
|
14
|
+
end
|
15
|
+
rescue Pry::Pager::StopPaging, Interrupt
|
16
|
+
pager.fast_exit!
|
17
|
+
system 'reset'
|
18
|
+
rescue Timeout::Error
|
19
|
+
pager.fast_exit!
|
20
|
+
system 'reset'
|
21
|
+
retry
|
22
|
+
ensure
|
23
|
+
pager.close
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
# @api private
|
28
|
+
def __render_tweets(title, tweets)
|
29
|
+
title + tweets.map {|tweet|
|
30
|
+
__render_tweet(tweet)
|
31
|
+
}.join("\n")
|
32
|
+
end
|
33
|
+
|
34
|
+
# @api private
|
35
|
+
def __render_tweet(tweet)
|
36
|
+
contents = __read_tweet_body(tweet)
|
37
|
+
body = "#{tweet.url}\n--\n#{contents}\n"
|
38
|
+
height = body.lines.count > box_height ? body.lines.count : box_height
|
39
|
+
TTY::Box.frame(height: height,
|
40
|
+
width: box_width,
|
41
|
+
title: {top_left: __render_tweet_title(tweet)}) {body}.to_s
|
42
|
+
end
|
43
|
+
|
44
|
+
# @api private
|
45
|
+
def __render_tweet_title(tweet)
|
46
|
+
user, created_at = tweet.user, tweet.created_at.getlocal
|
47
|
+
title = [
|
48
|
+
red("@#{user.screen_name}"),
|
49
|
+
"Around " + time_ago_in_words(created_at) + " before Last Refresh"
|
50
|
+
].join green(" | ")
|
51
|
+
" #{title} "
|
52
|
+
end
|
53
|
+
|
54
|
+
# @api private
|
55
|
+
def __read_tweet_body(tweet)
|
56
|
+
uris = tweet.uris
|
57
|
+
text = tweet.attrs[:full_text] ? tweet.attrs[:full_text] :
|
58
|
+
tweet.full_text
|
59
|
+
# 'text' might be a frozen string
|
60
|
+
text = text.dup
|
61
|
+
uris.each do |uri|
|
62
|
+
text.gsub!(uri.attrs[:url], uri.attrs[:expanded_url])
|
63
|
+
end
|
64
|
+
CGI.unescapeHTML(text).strip
|
65
|
+
end
|
66
|
+
|
67
|
+
# @api private
|
68
|
+
def __fetch_tweets(tweet_fetcher)
|
69
|
+
if tweet_fetcher.respond_to?(:call)
|
70
|
+
tweet_fetcher.call
|
71
|
+
else
|
72
|
+
# Already fetched
|
73
|
+
tweet_fetcher
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# @api private
|
78
|
+
def __choose_title(title, began_at, refresh_at)
|
79
|
+
title = bold green(title || "Twitter")
|
80
|
+
timestamps = [bold("Last Refresh: "), began_at.strftime(time_format), "\n"]
|
81
|
+
timestamps.concat [
|
82
|
+
bold("Next Refresh: "),
|
83
|
+
refresh_at.strftime(time_format),
|
84
|
+
"\n"
|
85
|
+
] if refresh_at
|
86
|
+
title = "#{title}\n\n"
|
87
|
+
title += timestamps.join
|
88
|
+
title << "\n\n"
|
89
|
+
title
|
90
|
+
end
|
91
|
+
|
92
|
+
# @api private
|
93
|
+
def __choose_render_interval(timeout)
|
94
|
+
return nil if timeout == false
|
95
|
+
timeout || (60*5)
|
96
|
+
end
|
97
|
+
|
98
|
+
# @api private
|
99
|
+
def __find_timeout_range(seconds)
|
100
|
+
seconds ? [Time.now.getlocal, (Time.now + seconds).getlocal] :
|
101
|
+
[Time.now.getlocal, nil]
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Pry::SendTweet::UserRenderer
|
2
|
+
def render_user(user)
|
3
|
+
title = "@#{user.screen_name} ( https://twitter.com/#{user.screen_name} )"
|
4
|
+
body = CGI.unescapeHTML <<-USER.each_line.map(&:strip).join("\n")
|
5
|
+
#{bold("Tweets")} #{user.tweets_count}
|
6
|
+
#{bold("Followers")} #{user.followers_count}
|
7
|
+
#{bold("Following")} #{user.friends_count}
|
8
|
+
#{user.description}
|
9
|
+
USER
|
10
|
+
height = body.lines.count > box_height ? body.lines.count : box_height
|
11
|
+
TTY::Box.frame(
|
12
|
+
height: height,
|
13
|
+
width: box_width,
|
14
|
+
title: {top_left: title}
|
15
|
+
) { body }.to_s
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
#
|
2
|
+
# A monkey patch module that implements unicode & emoji support in the gem
|
3
|
+
# 'tty-box' when using its `frame` method.
|
4
|
+
#
|
5
|
+
module Pry::SendTweet::TTYPatch
|
6
|
+
require 'tty-box'
|
7
|
+
require 'unicode/display_width'
|
8
|
+
# Create a frame
|
9
|
+
#
|
10
|
+
# @api public
|
11
|
+
def frame(top: nil, left: nil, width: 35, height: 3, align: :left, padding: 0,
|
12
|
+
title: {}, border: :light, style: {})
|
13
|
+
output = []
|
14
|
+
content = []
|
15
|
+
position = top && left
|
16
|
+
|
17
|
+
border = TTY::Box::Border.parse(border)
|
18
|
+
top_size = border.top? ? 1: 0
|
19
|
+
bottom_size = border.bottom? ? 1: 0
|
20
|
+
left_size = border.left? ? 1 : 0
|
21
|
+
right_size = border.right ? 1 : 0
|
22
|
+
|
23
|
+
if block_given?
|
24
|
+
content = format(yield, width, padding, align)
|
25
|
+
end
|
26
|
+
|
27
|
+
fg, bg = *extract_style(style)
|
28
|
+
border_fg, border_bg = *extract_style(style[:border] || {})
|
29
|
+
|
30
|
+
if border.top?
|
31
|
+
output << cursor.move_to(left, top) if position
|
32
|
+
output << top_border(title, width, border, style)
|
33
|
+
output << "\n" unless position
|
34
|
+
end
|
35
|
+
|
36
|
+
(height - top_size - bottom_size).times do |i|
|
37
|
+
output << cursor.move_to(left, top + i + top_size) if position
|
38
|
+
if border.left?
|
39
|
+
output << border_bg.(border_fg.(pipe_char(border.type)))
|
40
|
+
end
|
41
|
+
|
42
|
+
content_size = width - left_size - right_size
|
43
|
+
unless content[i].nil?
|
44
|
+
output << bg.(fg.(content[i]))
|
45
|
+
plain_content = Pry::Helpers::Text.strip_color(content[i])
|
46
|
+
content_size -= Unicode::DisplayWidth.of(plain_content, 1, {})
|
47
|
+
end
|
48
|
+
if style[:fg] || style[:bg] || !position # something to color
|
49
|
+
output << bg.(fg.(' ' * content_size))
|
50
|
+
end
|
51
|
+
|
52
|
+
if border.right?
|
53
|
+
if position
|
54
|
+
output << cursor.move_to(left + width - right_size, top + i + top_size)
|
55
|
+
end
|
56
|
+
output << border_bg.(border_fg.(pipe_char(border.type)))
|
57
|
+
end
|
58
|
+
output << "\n" unless position
|
59
|
+
end
|
60
|
+
|
61
|
+
if border.bottom?
|
62
|
+
output << cursor.move_to(left, top + height - bottom_size) if position
|
63
|
+
output << bottom_border(title, width, border, style)
|
64
|
+
output << "\n" unless position
|
65
|
+
end
|
66
|
+
output.join
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
module TTY::Box
|
71
|
+
prepend Pry::SendTweet::TTYPatch
|
72
|
+
singleton_class.module_eval { prepend Pry::SendTweet::TTYPatch }
|
73
|
+
end
|