termtter 1.7.2 → 1.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. data/.gitignore +1 -1
  2. data/README.rdoc +1 -0
  3. data/Rakefile +2 -1
  4. data/VERSION +1 -1
  5. data/bin/termtter +1 -0
  6. data/bin/termtter_frame +134 -0
  7. data/lib/plugins/aa.rb +44 -0
  8. data/lib/plugins/another_prompt.rb +42 -41
  9. data/lib/plugins/appendtitle.rb +82 -0
  10. data/lib/plugins/ar.rb +11 -8
  11. data/lib/plugins/async.rb +3 -2
  12. data/lib/plugins/capital_update.rb +12 -0
  13. data/lib/plugins/channel.rb +149 -0
  14. data/lib/plugins/clock.rb +10 -0
  15. data/lib/plugins/defaults/command_line.rb +8 -0
  16. data/lib/plugins/defaults/confirm.rb +1 -1
  17. data/lib/plugins/defaults/hashtag.rb +1 -1
  18. data/lib/plugins/defaults/keyword.rb +11 -0
  19. data/lib/plugins/defaults/list.rb +32 -6
  20. data/lib/plugins/defaults/standard_commands.rb +135 -52
  21. data/lib/plugins/defaults/standard_completion.rb +14 -0
  22. data/lib/plugins/defaults/stdout.rb +59 -27
  23. data/lib/plugins/defaults/user.rb +32 -0
  24. data/lib/plugins/draft.rb +9 -12
  25. data/lib/plugins/easy_post.rb +5 -0
  26. data/lib/plugins/event_invoked_at.rb +23 -0
  27. data/lib/plugins/expand-tinyurl.rb +13 -20
  28. data/lib/plugins/favotter.rb +9 -9
  29. data/lib/plugins/footer.rb +9 -12
  30. data/lib/plugins/friends.rb +0 -26
  31. data/lib/plugins/grass.rb +2 -4
  32. data/lib/plugins/growl.rb +47 -0
  33. data/lib/plugins/gyazo.rb +16 -18
  34. data/lib/plugins/hi.rb +31 -10
  35. data/lib/plugins/http_server.rb +3 -2
  36. data/lib/plugins/irc_gw.rb +71 -17
  37. data/lib/plugins/line.rb +10 -0
  38. data/lib/plugins/linefeed.rb +6 -1
  39. data/lib/plugins/list_switch.rb +76 -0
  40. data/lib/plugins/nop.rb +9 -0
  41. data/lib/plugins/notify-send.rb +1 -1
  42. data/lib/plugins/notify-send2.rb +25 -16
  43. data/lib/plugins/notify-send3.rb +16 -13
  44. data/lib/plugins/nuance.rb +29 -0
  45. data/lib/plugins/open_url.rb +1 -5
  46. data/lib/plugins/random.rb +2 -6
  47. data/lib/plugins/reply_sound.rb +33 -0
  48. data/lib/plugins/say.rb +2 -2
  49. data/lib/plugins/storage/sqlite3.rb +1 -1
  50. data/lib/plugins/story.rb +44 -0
  51. data/lib/plugins/tinyurl.rb +50 -29
  52. data/lib/plugins/translate_tweet.rb +38 -0
  53. data/lib/plugins/web.rb +27 -0
  54. data/lib/termtter.rb +8 -4
  55. data/lib/termtter/client.rb +17 -21
  56. data/lib/termtter/command.rb +35 -13
  57. data/lib/termtter/config.rb +13 -0
  58. data/lib/termtter/config_template.erb +3 -2
  59. data/lib/termtter/default_config.rb +2 -2
  60. data/lib/termtter/event.rb +69 -0
  61. data/lib/termtter/hook.rb +6 -1
  62. data/lib/termtter/hookable.rb +9 -1
  63. data/lib/termtter/httppool.rb +17 -9
  64. data/lib/termtter/optparse.rb +11 -1
  65. data/lib/termtter/rubytter_proxy.rb +21 -5
  66. data/spec/plugins/capital_update_spec.rb +9 -0
  67. data/spec/plugins/fib_spec.rb +2 -4
  68. data/spec/plugins/hi_spec.rb +9 -0
  69. data/spec/plugins/tinyurl_spec.rb +78 -0
  70. data/spec/termtter/client_spec.rb +5 -12
  71. data/spec/termtter/command_spec.rb +22 -10
  72. data/spec/termtter/config_spec.rb +23 -0
  73. data/spec/termtter/event_spec.rb +129 -0
  74. data/spec/termtter/optparse_spec.rb +2 -2
  75. data/spec/termtter/rubytter_proxy_spec.rb +1 -1
  76. metadata +39 -8
  77. data/bin/kill_termtter +0 -22
  78. data/lib/plugins/defaults/users.rb +0 -63
  79. data/lib/plugins/pause.rb +0 -3
  80. data/test/friends_timeline.json +0 -5
  81. data/test/search.json +0 -8
@@ -4,6 +4,20 @@ require 'set'
4
4
 
5
5
  module Termtter::Client
6
6
 
7
+ #
8
+ # completion for status ids
9
+ # (This is needed for some plugins)
10
+ #
11
+
12
+ public_storage[:status_ids] ||= Set.new
13
+
14
+ register_hook(:collect_status_ids, :point => :pre_filter) do |statuses, event|
15
+ statuses.each do |s|
16
+ public_storage[:status_ids].add(s.id)
17
+ public_storage[:status_ids].add(s.in_reply_to_status_id) if s.in_reply_to_status_id
18
+ end
19
+ end
20
+
7
21
  #
8
22
  # completion for user names
9
23
  #
@@ -10,7 +10,7 @@ config.plugins.stdout.set_default(
10
10
  [
11
11
  '<90><%=time%> [<%=status_id%>]</90> ',
12
12
  '<%= indent_text %>',
13
- '<<%=color%>><%=s.user.screen_name%>: <%=colorize_users(text)%></<%=color%>> ',
13
+ '<<%=color%>><%=s.user.screen_name%>: <%=text%></<%=color%>> ',
14
14
  '<90>',
15
15
  '<%=reply_to_status_id ? " (reply_to [#{reply_to_status_id}]) " : ""%>',
16
16
  '<%=retweeted_status_id ? " (retweet_to [#{retweeted_status_id}]) " : ""%>',
@@ -18,6 +18,8 @@ config.plugins.stdout.set_default(
18
18
  '</90>'
19
19
  ].join('')
20
20
  )
21
+ config.plugins.stdout.set_default(:sweets, %w[jugyo ujm sora_h lingr_termtter termtter hitode909 nanki sixeight])
22
+ config.plugins.stdout.set_default(:sweet_color, 'red')
21
23
  config.plugins.stdout.set_default(:time_format_today, '%H:%M:%S')
22
24
  config.plugins.stdout.set_default(:time_format_not_today, '%y/%m/%d %H:%M')
23
25
  config.plugins.stdout.set_default(:enable_pager, false)
@@ -28,6 +30,8 @@ config.plugins.stdout.set_default(:typable_id_prefix, '$')
28
30
  config.plugins.stdout.set_default(:show_reply_chain, true)
29
31
  config.plugins.stdout.set_default(:indent_format, %q("#{' ' * (indent - 1)} → "))
30
32
  config.plugins.stdout.set_default(:max_indent_level, 1)
33
+ config.plugins.stdout.set_default(
34
+ :screen_name_to_hash_proc, lambda { |screen_name| screen_name.to_i(36) })
31
35
 
32
36
  module Termtter
33
37
  class TypableIdGenerator
@@ -64,7 +68,8 @@ module Termtter
64
68
  @typable_id_generator = TypableIdGenerator.new(config.plugins.stdout.typable_ids)
65
69
 
66
70
  def self.data_to_typable_id(data)
67
- id = config.plugins.stdout.typable_id_prefix + @typable_id_generator.get_id(data)
71
+ id = config.plugins.stdout.typable_id_prefix +
72
+ @typable_id_generator.get_id(data)
68
73
  end
69
74
 
70
75
  def self.typable_id_to_data(id)
@@ -102,12 +107,14 @@ module Termtter
102
107
  output_text << status_line(s, time_format, event)
103
108
  end
104
109
 
105
- if config.plugins.stdout.enable_pager && ENV['LINES'] && statuses.size > ENV['LINES'].to_i
106
- file = Tempfile.new('termtter')
107
- file.print output_text
108
- file.close
109
- system "#{config.plugins.stdout.pager} #{file.path}"
110
- file.close(true)
110
+ if config.plugins.stdout.enable_pager &&
111
+ ENV['LINES'] &&
112
+ statuses.size > ENV['LINES'].to_i
113
+ file = Tempfile.new('termtter')
114
+ file.print output_text
115
+ file.close
116
+ system "#{config.plugins.stdout.pager} #{file.path}"
117
+ file.close(true)
111
118
  else
112
119
  print output_text
113
120
  end
@@ -116,7 +123,7 @@ module Termtter
116
123
  def status_line(s, time_format, event, indent = 0)
117
124
  return '' unless s
118
125
  text = TermColor.escape(s.text)
119
- color = config.plugins.stdout.colors[s.user.id.to_i % config.plugins.stdout.colors.size]
126
+ color = color_of_user(s.user)
120
127
  status_id = Termtter::Client.data_to_typable_id(s.id)
121
128
  reply_to_status_id =
122
129
  if s.in_reply_to_status_id
@@ -139,18 +146,25 @@ module Termtter
139
146
  when 'web' then 'web'
140
147
  end
141
148
 
149
+ text = colorize_users(text)
150
+ text = Client.get_hooks(:pre_coloring).inject(text) {|result, hook|
151
+ Termtter::Client.logger.debug "stdout status_line: call hook :pre_coloring #{hook.inspect}"
152
+ hook.call(result, event)
153
+ }
142
154
  indent_text = indent > 0 ? eval(config.plugins.stdout.indent_format) : ''
143
155
  erbed_text = ERB.new(config.plugins.stdout.timeline_format).result(binding)
144
-
145
- erbed_text = Client.get_hooks(:pre_coloring).inject(erbed_text){|result, hook| hook.call(result, event)}
146
-
156
+ erbed_text = Client.get_hooks(:pre_output).inject(erbed_text) {|result, hook|
157
+ Termtter::Client.logger.debug "stdout status_line: call hook :pre_output #{hook.inspect}"
158
+ hook.call(result, event)
159
+ }
147
160
  text = TermColor.unescape(TermColor.parse(erbed_text) + "\n")
148
161
  if config.plugins.stdout.show_reply_chain && s.in_reply_to_status_id
149
162
  indent += 1
150
163
  unless indent > config.plugins.stdout.max_indent_level
151
164
  begin
152
- status = Termtter::API.twitter.show(s.in_reply_to_status_id)
153
- text << status_line(status, time_format, event, indent)
165
+ if status = Termtter::API.twitter.cached_status(s.in_reply_to_status_id)
166
+ text << status_line(status, time_format, event, indent)
167
+ end
154
168
  rescue Rubytter::APIError
155
169
  end
156
170
  end
@@ -159,19 +173,35 @@ module Termtter
159
173
  end
160
174
 
161
175
  def colorize_users(text)
162
- text.gsub(/@(\w+)/) do |i|
163
- user = Termtter::API.twitter.cached_user($1)
164
- if user
165
- color = user_color(user)
166
- "<#{color}>#{i}</#{color}>"
167
- else
168
- i
169
- end
176
+ text.gsub(/@([0-9A-Za-z_]+)/) do |i|
177
+ color = color_of_screen_name($1)
178
+ "<#{color}>#{i}</#{color}>"
170
179
  end
171
180
  end
172
181
 
173
- def user_color(user)
174
- config.plugins.stdout.colors[user.id.to_i % config.plugins.stdout.colors.size]
182
+ def color_of_user(user)
183
+ color_of_screen_name(user.screen_name)
184
+ end
185
+
186
+ def color_of_screen_name(screen_name)
187
+ return color_of_screen_name_cache[screen_name] if
188
+ color_of_screen_name_cache.key?(screen_name)
189
+ num = screen_name_to_hash(screen_name)
190
+ color = config.plugins.stdout.instance_eval {
191
+ sweets.include?(screen_name) ?
192
+ sweet_color : colors[num % colors.size]
193
+ }
194
+ color_of_screen_name_cache[screen_name] = color
195
+ color_of_screen_name_cache[screen_name]
196
+ end
197
+
198
+ def screen_name_to_hash(screen_name)
199
+ config.plugins.stdout.screen_name_to_hash_proc.
200
+ call(screen_name)
201
+ end
202
+
203
+ def color_of_screen_name_cache
204
+ @color_of_screen_name_cache ||= {}
175
205
  end
176
206
  end
177
207
 
@@ -196,6 +226,8 @@ end
196
226
  # stdout.rb
197
227
  # output statuses to stdout
198
228
  # example config
199
- # config.plugins.stdout.colors = [:none, :red, :green, :yellow, :blue, :magenta, :cyan]
200
- # config.plugins.stdout.timeline_format = '<90><%=time%> [<%=status_id%>]</90> <<%=color%>><%=s.user.screen_name%>: <%=text%></<%=color%>> ' +
201
- # '<90><%=reply_to_status_id ? " (reply_to [#{reply_to_status_id}]) " : ""%><%=source%></90>'
229
+ # config.plugins.stdout.colors =
230
+ # [:none, :red, :green, :yellow, :blue, :magenta, :cyan]
231
+ # config.plugins.stdout.timeline_format =
232
+ # '<90><%=time%> [<%=status_id%>]</90> <<%=color%>><%=s.user.screen_name%>: <%=text%></<%=color%>> ' +
233
+ # '<90><%=reply_to_status_id ? " (reply_to [#{reply_to_status_id}]) " : ""%><%=source%></90>'
@@ -0,0 +1,32 @@
1
+ module Termtter::Client
2
+ register_command(
3
+ 'user show',
4
+ :aliases => [:p, :profile],
5
+ :help => ["user show USERNAME", "Show user's profile."]
6
+ ) do |arg|
7
+ user_name = arg.empty? ? config.user_name : arg
8
+ user = Termtter::API.twitter.user(user_name)
9
+ attrs = %w[ name screen_name url description profile_image_url location protected following
10
+ friends_count followers_count statuses_count favourites_count
11
+ id time_zone created_at utc_offset notifications
12
+ ]
13
+ label_width = attrs.map(&:size).max
14
+ attrs.each do |attr|
15
+ value = user.__send__(attr.to_sym)
16
+ puts "#{attr.gsub('_', ' ').rjust(label_width)}: #{value}"
17
+ end
18
+ end
19
+
20
+ class UserSearchEvent; attr_reader :query; def initialize(query); @query = query end; end
21
+
22
+ register_command(
23
+ 'user search',
24
+ :help => ["user search QUERY", "search users"]
25
+ ) do |arg|
26
+ search_option = config.user_search.option.empty? ? {} : config.user_search.option
27
+ users = Termtter::API.twitter.search_user(arg, search_option)
28
+ users.each do |user|
29
+ puts "@#{user.screen_name} (#{user.name}): #{user.description}"
30
+ end
31
+ end
32
+ end
data/lib/plugins/draft.rb CHANGED
@@ -34,22 +34,19 @@ module Termtter::Client
34
34
 
35
35
  register_command('draft exec') do |arg|
36
36
  index = get_draft_index(arg)
37
- if index
38
- command = public_storage[:drafts][index]
39
- if command
40
- puts "exec => \"#{command}\""
41
- execute(public_storage[:drafts][index])
42
- public_storage[:drafts].delete_at(index)
43
- end
44
- end
37
+ break unless index
38
+ command = public_storage[:drafts][index]
39
+ break unless command
40
+ puts "exec => \"#{command}\""
41
+ execute(public_storage[:drafts][index])
42
+ public_storage[:drafts].delete_at(index)
45
43
  end
46
44
 
47
45
  register_command('draft delete') do |arg|
48
46
  index = get_draft_index(arg)
49
- if index
50
- deleted = public_storage[:drafts].delete_at(index)
51
- puts "deleted => \"#{deleted}\"" if deleted
52
- end
47
+ break unless index
48
+ deleted = public_storage[:drafts].delete_at(index)
49
+ puts "deleted => \"#{deleted}\"" if deleted
53
50
  end
54
51
 
55
52
  register_command('draft clear') do |arg|
@@ -0,0 +1,5 @@
1
+ module Termtter::Client
2
+ register_hook(:easy_post, :point => :command_not_found) do |text|
3
+ execute("update #{text}")
4
+ end
5
+ end
@@ -0,0 +1,23 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module Termtter::Client
4
+
5
+ register_hook(
6
+ :name => :set_invoked_at,
7
+ :point => :pre_filter,
8
+ :exec_proc => lambda do |statuses, event|
9
+ event[:invoked_at] = Time.now
10
+ statuses
11
+ end
12
+ )
13
+
14
+ register_hook(
15
+ :name => :print_invoked_at,
16
+ :point => :post_filter,
17
+ :exec_proc => lambda do |statuses, event|
18
+ puts "event was invoked at #{event.invoked_at}."
19
+ statuses
20
+ end
21
+ )
22
+
23
+ end
@@ -1,30 +1,22 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
3
  URL_SHORTTERS = [
4
- { :host => "tinyurl.com", :pattern => %r'(http://tinyurl\.com(/[\w/]+))' },
5
- { :host => "is.gd", :pattern => %r'(http://is\.gd(/[\w/]+))' },
6
- { :host => "bit.ly", :pattern => %r'(http://bit\.ly(/[\w/]+))' },
7
- { :host => "ff.im", :pattern => %r'(http://ff\.im(/[-\w/]+))'},
8
- { :host => "j.mp", :pattern => %r'(http://j\.mp(/[\w/]+))' },
9
- { :host => "goo.gl", :pattern => %r'(http://goo\.gl(/[\w/]+))' },
4
+ {:host => "tinyurl.com", :pattern => %r'(http://tinyurl\.com(/[\w/]+))'},
5
+ {:host => "is.gd", :pattern => %r'(http://is\.gd(/[\w/]+))'},
6
+ {:host => "bit.ly", :pattern => %r'(http://bit\.ly(/[\w/]+))'},
7
+ {:host => "ff.im", :pattern => %r'(http://ff\.im(/[-\w/]+))'},
8
+ {:host => "j.mp", :pattern => %r'(http://j\.mp(/[\w/]+))'},
9
+ {:host => "goo.gl", :pattern => %r'(http://goo\.gl(/[\w/]+))'},
10
+ {:host => "tr.im", :pattern => %r'(http://tr\.im(/[\w/]+))'},
11
+ {:host => "short.to", :pattern => %r'(http://short\.to(/[\w/]+))'},
12
+ {:host => "ow.ly", :pattern => %r'(http://ow\.ly(/[\w/]+))'},
13
+ {:host => "u.nu", :pattern => %r'(http://u\.nu(/[\w/]+))'},
14
+ {:host => "twurl.nl", :pattern => %r'(http://twurl\.nl(/\w+))'},
10
15
  ]
11
16
 
12
17
  config.plugins.expand_tinyurl.set_default(:shortters, [])
13
18
  config.plugins.expand_tinyurl.set_default(:skip_users, [])
14
19
 
15
- # for Ruby 1.8
16
- unless String.public_method_defined?(:force_encoding)
17
- class String
18
- def force_encoding(enc)
19
- self
20
- end
21
- end
22
-
23
- module Encoding
24
- UTF_8 = nil
25
- end
26
- end
27
-
28
20
  Termtter::Client::register_hook(
29
21
  :name => :expand_tinyurl,
30
22
  :point => :filter_for_output,
@@ -48,7 +40,8 @@ def expand_url(host, path)
48
40
  h.get(path, { 'User-Agent' => 'Mozilla' })
49
41
  end
50
42
  return nil unless res.code == "301" or res.code == "302"
51
- res['Location'].force_encoding(Encoding::UTF_8)
43
+ newurl = res['Location']
44
+ newurl.respond_to?(:force_encoding) ? newurl.force_encoding(Encoding::UTF_8) : newurl
52
45
  rescue Exception => e
53
46
  Termtter::Client.handle_error(e)
54
47
  nil
@@ -5,7 +5,6 @@ require 'nokogiri'
5
5
  require 'open-uri'
6
6
 
7
7
  module Termtter::Client
8
-
9
8
  public_storage[:favorited_ids] = {}
10
9
 
11
10
  class << self
@@ -28,11 +27,12 @@ module Termtter::Client
28
27
  end
29
28
 
30
29
  private
30
+
31
31
  def parse(url)
32
32
  doc = Nokogiri(open(url).read)
33
33
  doc.css('div.entry').map do |entry|
34
34
  id = entry['id'].gsub(/\Astatus_/, '')
35
- text = entry.css('span.status_text').first.content
35
+ text = CGI.unescapeHTML(entry.css('span.status_text').first.content)
36
36
  amount = entry.css('div.info span.favotters').first.content
37
37
  amount = amount.match(/(\d+)/)[1].to_i
38
38
  users = entry.css('div.info span.favotters img').map {|u| u['title'] }
@@ -53,13 +53,14 @@ module Termtter::Client
53
53
 
54
54
  help = ['favotter [USERNAME] [THRESHOLD]', 'Show info from favotter']
55
55
  register_command('favotter', :help => help) do |arg|
56
- target = if arg.empty?
56
+ target =
57
+ if arg.empty?
57
58
  config.user_name
58
- else
59
- args = arg.split
60
- threshold = args.size == 1 ? 1 : args[1]
61
- args[0]
62
- end
59
+ else
60
+ args = arg.split
61
+ threshold = args.size == 1 ? 1 : args[1]
62
+ args[0]
63
+ end
63
64
  if /@(.*)/ =~ target
64
65
  target = $1
65
66
  end
@@ -74,4 +75,3 @@ module Termtter::Client
74
75
  end
75
76
  end
76
77
  end
77
-
@@ -1,22 +1,19 @@
1
1
  #-*- coding: utf-8 -*-
2
-
3
- config.set_default('footer',nil)
2
+ config.set_default('footer', nil)
4
3
 
5
4
  Termtter::Client.register_hook(
6
5
  :name => :add_footer,
7
6
  :points => [:modify_arg_for_update, :modify_arg_for_reply],
8
- :exec => lambda {|cmd, arg|
9
- (config.footer.nil? || config.footer.empty?) ? arg : arg + " #{config.footer}"
10
- }
11
- )
7
+ :exec => lambda {|cmd, arg|
8
+ (config.footer.nil? || config.footer.empty?) ?
9
+ arg : arg + " #{config.footer}"
10
+ })
12
11
 
13
12
  Termtter::Client.register_command(
14
13
  :name => :footer,
15
14
  :aliases => [:ft],
16
15
  :exec => lambda {|arg|
17
- arg.empty? ? config.footer = nil : config.footer = arg
18
- puts config.footer.nil? ? "footer is turned off" : "new footer=> #{config.footer}"
19
- }
20
- )
21
-
22
-
16
+ arg.empty? ? config.footer = nil : config.footer = arg
17
+ puts config.footer.nil? ?
18
+ "footer is turned off" : "new footer=> #{config.footer}"
19
+ })
@@ -1,31 +1,5 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  module Termtter::Client
3
-
4
- class << self
5
- def get_friends(user_name, max)
6
- friends = []
7
- cursor = -1
8
- begin
9
- tmp = Termtter::API::twitter.friends(user_name, :cursor => cursor)
10
- cursor = tmp[:next_cursor]
11
- friends += tmp[:users]
12
- puts "#{friends.length}/#{max}"
13
- rescue
14
- end until (cursor.zero? or friends.length > max)
15
- friends.take(max)
16
- end
17
- end
18
-
19
- register_command(
20
- :name => :friends, :aliases => [:following],
21
- :exec_proc => lambda {|arg|
22
- user_name = arg.empty? ? config.user_name : arg
23
- public_storage[:friends] = friends = get_friends(user_name, 2000)
24
- puts friends.map(&:screen_name).join(' ')
25
- },
26
- :help => ["friends,following [USERNAME]", "Show user's friends."]
27
- )
28
-
29
3
  register_command(
30
4
  :name => :diff_follow, :aliases => [:diff],
31
5
  :exec_proc => lambda {|arg|
data/lib/plugins/grass.rb CHANGED
@@ -1,21 +1,19 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  module Termtter
3
3
  module Client
4
-
5
4
  config.plugins.grass.set_default(:rate, 0)
6
5
 
7
6
  register_command(
8
7
  :name => :w, :aliases => [:grass],
9
8
  :exec_proc => lambda {|arg|
10
9
  arg, rate = arg.split(/ /)
11
- count = arg =~ /^[0-9]+$/ ? arg.to_i : 3
10
+ count = /^[0-9]+$/ =~ arg ? arg.to_i : 3
12
11
  rate ||= config.plugins.grass.rate
13
12
  grow = (count * rate.to_i).quo(100).round
14
13
  grasses = ('w' * (count-grow) + 'W' * grow).split(//).shuffle.join
15
14
  execute("update #{grasses}")
16
15
  },
17
- :help => ['grass, w', 'Grass it!']
18
- )
16
+ :help => ['grass, w', 'Grass it!'])
19
17
  end
20
18
  end
21
19