pry-twitter 1.22.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.
Files changed (60) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +13 -0
  3. data/LICENSE.txt +24 -0
  4. data/PRY_LICENSE.txt +25 -0
  5. data/README.md +546 -0
  6. data/Vagrantfile +48 -0
  7. data/art/16-60174b5812.jpg +0 -0
  8. data/art/16-a31deea6f0.jpg +0 -0
  9. data/art/17-b3d5d6f6c2.jpg +0 -0
  10. data/art/44-e44743a5bb.jpg +0 -0
  11. data/art/51-5e4dc5fd92.jpg +0 -0
  12. data/art/51-82bde81305.jpg +0 -0
  13. data/art/51-ad821fa377.jpg +0 -0
  14. data/art/51-b90d5dd6a6.jpg +0 -0
  15. data/art/51-da43a4ef4f.jpg +0 -0
  16. data/art/51-e94c3387e1.jpg +0 -0
  17. data/art/52-eec4df2edf.jpg +0 -0
  18. data/art/53-9d231893e1.jpg +0 -0
  19. data/art/54-39b1063483.jpg +0 -0
  20. data/art/55-e41f9896d0.jpg +0 -0
  21. data/art/56-c82d4e5b61.jpg +0 -0
  22. data/art/59-2a2c9cd962.jpg +0 -0
  23. data/art/61-3970c79d47.jpg +0 -0
  24. data/art/62-5704cc1652.jpg +0 -0
  25. data/art/62-b0aceb0fa6.jpg +0 -0
  26. data/art/62-da0faf972e.jpg +0 -0
  27. data/art/frankewilde.jpg +0 -0
  28. data/art/rollingstone.jpg +0 -0
  29. data/art/tide.jpg +0 -0
  30. data/art/windows10.png +0 -0
  31. data/lib/pry-send_tweet.rb +77 -0
  32. data/lib/pry/pager/system_pager.rb +25 -0
  33. data/lib/pry/send_tweet/commands/base_command.rb +72 -0
  34. data/lib/pry/send_tweet/commands/easter_eggs.rb +564 -0
  35. data/lib/pry/send_tweet/commands/paging/paging_support.rb +16 -0
  36. data/lib/pry/send_tweet/commands/read_tweets.rb +93 -0
  37. data/lib/pry/send_tweet/commands/read_tweets/translate_actions.rb +94 -0
  38. data/lib/pry/send_tweet/commands/send_tweet.rb +164 -0
  39. data/lib/pry/send_tweet/commands/twitter_action.rb +118 -0
  40. data/lib/pry/send_tweet/commands/twitter_action/delete_tweet_actions.rb +19 -0
  41. data/lib/pry/send_tweet/commands/twitter_action/follow_actions.rb +66 -0
  42. data/lib/pry/send_tweet/commands/twitter_action/like_actions.rb +23 -0
  43. data/lib/pry/send_tweet/commands/twitter_action/mute_actions.rb +23 -0
  44. data/lib/pry/send_tweet/commands/twitter_action/profile_actions.rb +60 -0
  45. data/lib/pry/send_tweet/commands/twitter_action/suggested_actions.rb +20 -0
  46. data/lib/pry/send_tweet/commands/twitter_search.rb +36 -0
  47. data/lib/pry/send_tweet/renderers/tweet_renderer.rb +103 -0
  48. data/lib/pry/send_tweet/renderers/user_renderer.rb +17 -0
  49. data/lib/pry/send_tweet/tty-box.rb +73 -0
  50. data/lib/pry/send_tweet/twitter_io.rb +26 -0
  51. data/lib/pry/send_tweet/version.rb +6 -0
  52. data/lib/time-ago-in-words/README.md +14 -0
  53. data/lib/time-ago-in-words/lib/time-ago-in-words.rb +37 -0
  54. data/lib/time-ago-in-words/time-ago-in-words.gemspec +14 -0
  55. data/pry-send_tweet.gemspec +23 -0
  56. data/samples/freebsd-zshrc +5 -0
  57. data/samples/loader.conf +1 -0
  58. data/samples/tmuxinator-vagrant.yml +11 -0
  59. data/vms/freebsd.rb +18 -0
  60. metadata +145 -0
@@ -0,0 +1,16 @@
1
+ module Pry::SendTweet::PagingSupport
2
+ def page(str)
3
+ _pry_.pager.page(str.to_s)
4
+ end
5
+
6
+ def page_ok(str)
7
+ prefix = bright_green "OK: "
8
+ page "#{prefix}#{str}"
9
+ end
10
+
11
+ def page_error(str)
12
+ str = str.respond_to?(:message) ? str.message : str
13
+ prefix = bright_red "Error: "
14
+ page "#{prefix}#{str}"
15
+ end
16
+ end
@@ -0,0 +1,93 @@
1
+ class Pry::SendTweet::ReadTweets < Pry::SendTweet::BaseCommand
2
+ require_relative 'read_tweets/translate_actions'
3
+ include TranslateActions
4
+
5
+ match 'read-tweets'
6
+ description 'Read tweets.'
7
+ banner <<-BANNER
8
+ read-tweets [OPTIONS]
9
+
10
+ #{description}
11
+ BANNER
12
+
13
+ def options(o)
14
+ o.on 't=', 'tweeter=',
15
+ 'A username whose timeline you want to read.'
16
+ o.on 'c=', 'count=',
17
+ "The number of tweets to read. The maximum is 200, and the default is " \
18
+ "#{default_read_size}."
19
+ o.on 'l=', 'likes=',
20
+ 'Read tweets you or another user have liked.',
21
+ argument: :optional
22
+ o.on 'r=', 'replies=', 'A username whose replies you want to read.'
23
+ o.on 'm', 'mentions', 'Read tweets that @mention you.'
24
+ o.on 'x=', 'translate=', 'Translate a tweet.'
25
+ o.on 'tx=', nil, 'Translate a string of text.'
26
+ o.on 'sl=', 'source-lang=', '[optional] The source language of the ' \
27
+ 'text or tweet being translated'
28
+ o.on 'w', 'with-retweets', 'Include retweets.'
29
+ end
30
+
31
+ def process
32
+ super
33
+ case
34
+ when opts.present?('translate') then translate_tweet(opts['x'], opts['sl'])
35
+ when opts.present?('tx') then translate_text(opts['tx'], opts['sl'])
36
+ when opts.present?('replies') then show_replies(user: opts['replies'])
37
+ when opts.present?('likes') then show_likes(user: opts['likes'])
38
+ when opts.present?('mentions') then show_mentions
39
+ else show_tweets_from_timeline(user: opts['tweeter'])
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ def show_replies(user:)
46
+ username = search_str_for_users(user).first
47
+ render_tweets lambda {
48
+ twitter.user_timeline(username, timeline_options).select {|tweet|
49
+ tweet.reply? &&
50
+ tweet.in_reply_to_screen_name? &&
51
+ tweet.in_reply_to_screen_name.downcase != username.downcase
52
+ }
53
+ },
54
+ title: "#{'@'+user} replies"
55
+ end
56
+
57
+ def show_likes(user:)
58
+ if user
59
+ user = search_str_for_users(user).first
60
+ render_tweets lambda { twitter.favorites(user, count: opts['count'] || default_read_size)},
61
+ title: "#{'@'+user} likes"
62
+ else
63
+ render_tweets lambda { twitter.favorites(count: opts['count'] || default_read_size) },
64
+ title: "Your likes"
65
+ end
66
+ end
67
+
68
+ def show_tweets_from_timeline(user:)
69
+ if user
70
+ user = search_str_for_users(user).first
71
+ render_tweets lambda { twitter.user_timeline(user, timeline_options) },
72
+ title: '@'+user
73
+ else
74
+ render_tweets lambda { twitter.home_timeline(timeline_options) },
75
+ title: "Twitter"
76
+ end
77
+ end
78
+
79
+ def show_mentions
80
+ render_tweets lambda { twitter.mentions(timeline_options) },
81
+ title: "@mentions"
82
+ end
83
+
84
+ def timeline_options
85
+ {
86
+ tweet_mode: 'extended',
87
+ include_rts: opts.present?('with-retweets'),
88
+ count: opts.present?('count') ? opts['count'] : default_read_size
89
+ }
90
+ end
91
+
92
+ Pry.commands.add_command self
93
+ end
@@ -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)}: ", timeout: false
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,164 @@
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
+ o.on 'v', 'version', 'Print the version information of fry-send_tweet.rb'
29
+ end
30
+
31
+ def process(args)
32
+ super
33
+ return __print_version_information if opts.version?
34
+ tweet_creator = create_tweet_creator(compose_tweet_with_editor)
35
+ if opts.present?('delay')
36
+ time_obj, sleep_seconds = parse_duration_str(opts['delay'])
37
+ Thread.new {
38
+ sleep sleep_seconds
39
+ tweet_creator.call
40
+ }
41
+ publish_time = (time_obj ? time_obj : Time.now + sleep_seconds)
42
+ .getlocal
43
+ .strftime(time_format)
44
+ page_ok bold("Tweet will be published at approximately #{publish_time}.")
45
+ else
46
+ tweet = tweet_creator.call
47
+ page_ok tweet.url
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ def paths_to_twitterio(paths)
54
+ paths.map do |path|
55
+ Pry::SendTweet::TwitterIO.new File.binread(path), File.basename(path)
56
+ end
57
+ end
58
+
59
+ def no_newline?
60
+ opts.present?('no-newline')
61
+ end
62
+
63
+ def send_tweet_with_media(tweet_contents, medias)
64
+ tweet = twitter.update_with_media(tweet_contents, medias, tweet_options)
65
+ tweet.tap {|t| self_destruct! t.id, opts['self-destruct'] }
66
+ ensure
67
+ medias.each(&:close)
68
+ end
69
+
70
+ def send_textual_tweet(tweet_contents)
71
+ tweet = twitter.update(tweet_contents, tweet_options)
72
+ tweet.tap {|t| self_destruct! t.id, opts['self-destruct'] }
73
+ end
74
+
75
+ def prepend_username_to_reply!(tweet_url, file)
76
+ tweet = twitter.status(tweet_url)
77
+ mentions = [tweet.user.screen_name].concat tweet.user_mentions.map(&:screen_name)
78
+ file.write mentions.uniq(&:downcase).map{|u| "@#{u}" }.join(' ')
79
+ end
80
+
81
+ def replying_to_other_tweet?
82
+ opts.present?('reply-to')
83
+ end
84
+
85
+ def tweet_options
86
+ options = {}
87
+ options.merge!({
88
+ in_reply_to_status_id: Integer(File.basename(opts['reply-to']))
89
+ }) if replying_to_other_tweet?
90
+ options
91
+ end
92
+
93
+ def compose_tweet_with_editor
94
+ tweet = Tempfile.open('pry-send_tweet--compose-tweet') do |file|
95
+ if replying_to_other_tweet?
96
+ # During experimentation I noticed that without prefixing who we are
97
+ # replying to, the tweet we send will not be considered a reply even
98
+ # with reply_to_status_id being set.
99
+ prepend_username_to_reply!(opts['reply-to'], file)
100
+ end
101
+ file.rewind
102
+ Pry::Editor.new(_pry_).invoke_editor(file.path, 0)
103
+ lines = file.read.each_line
104
+ no_newline? ? lines.map(&:chomp).join(' ') : lines.to_a.join
105
+ end
106
+ validate_tweet!(tweet)
107
+ tweet
108
+ end
109
+
110
+ def create_tweet_creator(tweet_contents)
111
+ if opts.present?(:file)
112
+ medias = paths_to_twitterio(opts[:file])
113
+ lambda { send_tweet_with_media(tweet_contents, medias) }
114
+ else
115
+ lambda { send_textual_tweet(tweet_contents) }
116
+ end
117
+ end
118
+
119
+ def validate_tweet!(tweet)
120
+ if tweet.strip.empty?
121
+ raise Pry::CommandError, "Can't post an empty tweet."
122
+ elsif tweet.size > MAX_TWEET_SIZE
123
+ raise Pry::CommandError, "The tweet: \n" +
124
+ word_wrap(tweet) +
125
+ "\nis too big to publish, try to use less characters."
126
+ end
127
+ end
128
+
129
+ def self_destruct!(tweet_id, duration)
130
+ return if !duration
131
+ _, sleep_seconds = parse_duration_str(duration)
132
+ page bold("Tweet due to self destruct in #{sleep_seconds} seconds")
133
+ Thread.new do
134
+ sleep sleep_seconds
135
+ twitter.destroy_status(tweet_id)
136
+ end
137
+ end
138
+
139
+ def parse_duration_str(str)
140
+ if str =~ /\A\d+\z/
141
+ sleep_seconds = Integer(str)
142
+ elsif str =~ /\A\d{2}:\d{2}\z/ || str =~ /\A\d{2}:\d{2}:\d{2}\z/
143
+ time_obj = Time.parse(str)
144
+ time_obj += 3600*24 if time_obj <= Time.now
145
+ sleep_seconds = Integer(time_obj - Time.now)
146
+ else
147
+ raise Pry::CommandError, "--delay='#{str}' or --self-destruct='#{str}' is not " \
148
+ "something I understand."
149
+ end
150
+ [time_obj, sleep_seconds]
151
+ end
152
+
153
+ def __print_version_information
154
+ side1 = [
155
+ bright_green('fry'),
156
+ bold('-send_tweet.rb '),
157
+ bright_red("v#{Fry::SendTweet::VERSION}")
158
+ ].join
159
+ side2 = bright_yellow(Fry::SendTweet::CODENAME)
160
+ _pry_.pager.page [side1, side2].join(" | ")
161
+ end
162
+
163
+ Pry.commands.add_command self
164
+ 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