draftcode-termtter 1.0.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (118) hide show
  1. data/History.txt +4 -0
  2. data/README.rdoc +100 -0
  3. data/Rakefile +55 -0
  4. data/bin/kill_termtter +22 -0
  5. data/bin/termtter +6 -0
  6. data/lib/plugins/addspace.rb +27 -0
  7. data/lib/plugins/april_fool.rb +15 -0
  8. data/lib/plugins/bomb.rb +39 -0
  9. data/lib/plugins/clear.rb +14 -0
  10. data/lib/plugins/command_plus.rb +47 -0
  11. data/lib/plugins/confirm.rb +33 -0
  12. data/lib/plugins/cool.rb +10 -0
  13. data/lib/plugins/countter.rb +23 -0
  14. data/lib/plugins/devel.rb +18 -0
  15. data/lib/plugins/direct_messages.rb +36 -0
  16. data/lib/plugins/en2ja.rb +11 -0
  17. data/lib/plugins/english.rb +25 -0
  18. data/lib/plugins/erb.rb +17 -0
  19. data/lib/plugins/exec.rb +17 -0
  20. data/lib/plugins/exec_and_update.rb +14 -0
  21. data/lib/plugins/expand-tinyurl.rb +34 -0
  22. data/lib/plugins/fib.rb +28 -0
  23. data/lib/plugins/fib_filter.rb +14 -0
  24. data/lib/plugins/filter.rb +69 -0
  25. data/lib/plugins/graduatter.rb +8 -0
  26. data/lib/plugins/grass.rb +27 -0
  27. data/lib/plugins/group.rb +64 -0
  28. data/lib/plugins/growl.rb +52 -0
  29. data/lib/plugins/growl2.rb +143 -0
  30. data/lib/plugins/hatebu.rb +59 -0
  31. data/lib/plugins/hatebu_and_update.rb +58 -0
  32. data/lib/plugins/history.rb +93 -0
  33. data/lib/plugins/ignore.rb +19 -0
  34. data/lib/plugins/keyword.rb +18 -0
  35. data/lib/plugins/l2.rb +25 -0
  36. data/lib/plugins/log.rb +73 -0
  37. data/lib/plugins/me.rb +10 -0
  38. data/lib/plugins/modify_arg_hook_sample.rb +7 -0
  39. data/lib/plugins/msagent.rb +38 -0
  40. data/lib/plugins/multi_reply.rb +27 -0
  41. data/lib/plugins/notify-send.rb +22 -0
  42. data/lib/plugins/notify-send2.rb +52 -0
  43. data/lib/plugins/notify-send3.rb +45 -0
  44. data/lib/plugins/open_url.rb +59 -0
  45. data/lib/plugins/otsune.rb +21 -0
  46. data/lib/plugins/outputz.rb +33 -0
  47. data/lib/plugins/pause.rb +3 -0
  48. data/lib/plugins/post_exec_hook_sample.rb +9 -0
  49. data/lib/plugins/pre_exec_hook_sample.rb +9 -0
  50. data/lib/plugins/primes.rb +30 -0
  51. data/lib/plugins/quicklook.rb +41 -0
  52. data/lib/plugins/random.rb +23 -0
  53. data/lib/plugins/reblog.rb +40 -0
  54. data/lib/plugins/reload.rb +3 -0
  55. data/lib/plugins/reply.rb +8 -0
  56. data/lib/plugins/retweet.rb +46 -0
  57. data/lib/plugins/reverse.rb +13 -0
  58. data/lib/plugins/say.rb +26 -0
  59. data/lib/plugins/scrape.rb +41 -0
  60. data/lib/plugins/screen-notify.rb +20 -0
  61. data/lib/plugins/screen.rb +24 -0
  62. data/lib/plugins/shell.rb +14 -0
  63. data/lib/plugins/sl.rb +48 -0
  64. data/lib/plugins/spam.rb +16 -0
  65. data/lib/plugins/standard_plugins.rb +525 -0
  66. data/lib/plugins/stdout.rb +220 -0
  67. data/lib/plugins/storage.rb +55 -0
  68. data/lib/plugins/storage/DB.rb +37 -0
  69. data/lib/plugins/storage/status.rb +83 -0
  70. data/lib/plugins/storage/status_mook.rb +30 -0
  71. data/lib/plugins/switch_user.rb +22 -0
  72. data/lib/plugins/system_status.rb +33 -0
  73. data/lib/plugins/timer.rb +18 -0
  74. data/lib/plugins/tinyurl.rb +20 -0
  75. data/lib/plugins/translation.rb +38 -0
  76. data/lib/plugins/typable_id.rb +94 -0
  77. data/lib/plugins/update_editor.rb +53 -0
  78. data/lib/plugins/uri-open.rb +66 -0
  79. data/lib/plugins/wassr_post.rb +22 -0
  80. data/lib/plugins/yhara.rb +148 -0
  81. data/lib/plugins/yhara_filter.rb +8 -0
  82. data/lib/plugins/yonda.rb +21 -0
  83. data/lib/termtter.rb +38 -0
  84. data/lib/termtter/api.rb +57 -0
  85. data/lib/termtter/client.rb +324 -0
  86. data/lib/termtter/command.rb +80 -0
  87. data/lib/termtter/config.rb +68 -0
  88. data/lib/termtter/config_setup.rb +38 -0
  89. data/lib/termtter/connection.rb +41 -0
  90. data/lib/termtter/hook.rb +31 -0
  91. data/lib/termtter/optparse.rb +14 -0
  92. data/lib/termtter/system_extensions.rb +115 -0
  93. data/lib/termtter/task.rb +17 -0
  94. data/lib/termtter/task_manager.rb +116 -0
  95. data/lib/termtter/version.rb +4 -0
  96. data/spec/plugins/cool_spec.rb +10 -0
  97. data/spec/plugins/english_spec.rb +19 -0
  98. data/spec/plugins/fib_spec.rb +15 -0
  99. data/spec/plugins/filter_spec.rb +18 -0
  100. data/spec/plugins/pause_spec.rb +8 -0
  101. data/spec/plugins/primes_spec.rb +15 -0
  102. data/spec/plugins/shell_spec.rb +10 -0
  103. data/spec/plugins/sl_spec.rb +8 -0
  104. data/spec/plugins/spam_spec.rb +4 -0
  105. data/spec/plugins/standard_plugins_spec.rb +23 -0
  106. data/spec/plugins/storage/DB_spec.rb +12 -0
  107. data/spec/plugins/storage/status_spec.rb +24 -0
  108. data/spec/spec_helper.rb +7 -0
  109. data/spec/termtter/client_spec.rb +255 -0
  110. data/spec/termtter/command_spec.rb +107 -0
  111. data/spec/termtter/config_spec.rb +111 -0
  112. data/spec/termtter/hook_spec.rb +78 -0
  113. data/spec/termtter/task_manager_spec.rb +78 -0
  114. data/spec/termtter/task_spec.rb +22 -0
  115. data/spec/termtter_spec.rb +29 -0
  116. data/test/friends_timeline.json +5 -0
  117. data/test/search.json +8 -0
  118. metadata +211 -0
@@ -0,0 +1,13 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module Termtter::Client
4
+ add_filter do |statuses, _|
5
+ statuses.map do |s|
6
+ s.text = s.text.split(//).reverse.to_s
7
+ s
8
+ end
9
+ end
10
+ end
11
+
12
+ # filter-reverse.rb
13
+ # reverse texts
@@ -0,0 +1,26 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ raise 'say.rb runs only in OSX Leopard' if /darwin9/ !~ RUBY_PLATFORM
4
+
5
+ # say :: String -> String -> IO ()
6
+ def say(who, what)
7
+ voices = %w(Alex Alex Bruce Fred Ralph Agnes Kathy Vicki)
8
+ voice = voices[who.hash % voices.size]
9
+ system 'say', '-v', voice, what
10
+ end
11
+
12
+ module Termtter::Client
13
+ register_hook(
14
+ :name => :say,
15
+ :points => [:output],
16
+ :exec_proc => lambda {|statuses, event|
17
+ statuses.reverse.each do |s|
18
+ text_without_uri = s[:text].gsub(%r|https?://[^\s]+|, 'U.R.I.')
19
+ say s[:screen_name], text_without_uri
20
+ end
21
+ }
22
+ )
23
+ end
24
+
25
+ # KNOWN BUG:
26
+ # * exit or <C-c> doen't work quickly.
@@ -0,0 +1,41 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module Termtter::Client
4
+
5
+ def self.scrape_members(members)
6
+ statuses = []
7
+ members.each_with_index do |member, index|
8
+ puts "member #{index+1}/#{members.size} #{member}"
9
+ statuses += Termtter::API.twitter.user_timeline(member)
10
+ end
11
+ statuses
12
+ end
13
+
14
+ def self.scrape_group(group)
15
+ members = config.plugins.group.groups[group] || []
16
+ scrape_members(members)
17
+ end
18
+
19
+ register_command(
20
+ :name => :scrape_group,
21
+ :exec_proc => lambda{ |args|
22
+ groups = args.split(' ').map{|g| g.to_sym}
23
+ if groups.include? :all
24
+ groups = config.plugins.group.groups.keys
25
+ puts "get all groups..."
26
+ end
27
+ members = []
28
+ groups.each do |group|
29
+ members += config.plugins.group.groups[group]
30
+ end
31
+ statuses = scrape_members(members.uniq.compact.sort)
32
+ output(statuses_to_hash(statuses), :pre_filter)
33
+ },
34
+ :completion_proc => lambda {|cmd, args|
35
+ arg = args.split(' ').last
36
+ prefix = args.split(' ')[0..-2].join(' ')
37
+ find_group_candidates arg, "#{cmd} #{prefix} %s"
38
+ },
39
+ :help => ['scrape_group GROUPNAME(S)', 'Get the group timeline']
40
+ )
41
+ end
@@ -0,0 +1,20 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ config.screen_notify.set_default(:format, "[termtter] %s")
4
+
5
+ module Termtter::Client
6
+ register_hook(
7
+ :name => :screen_notify,
8
+ :points => [:output],
9
+ :exec_proc => lambda{|statuses, event|
10
+ return unless event = :update_friends_timeline
11
+ Thread.new(statuses) do |ss|
12
+ ss.each do |s|
13
+ msg = config.screen_notify.format % s.user.screen_name
14
+ system 'screen', '-X', 'eval', "bell_msg '#{msg}'", 'bell'
15
+ sleep 1
16
+ end
17
+ end
18
+ }
19
+ )
20
+ end
@@ -0,0 +1,24 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module Termtter
4
+ module Plugin
5
+ module Screen
6
+ def self.set_title(title)
7
+ print "\033k#{title}\033\\\n"
8
+ end
9
+ end
10
+ end
11
+ end
12
+
13
+ # Add below to your ~/.termtter
14
+ #
15
+ # require 'plugins/yonda'
16
+ # require 'plugins/screen'
17
+ # module Termtter::Client
18
+ # register_hook(:name => :screen,
19
+ # :points => [:post_exec__update_timeline, :plugin_yonda_yonda, :post_exec_yonda],
20
+ # :exec_proc => lambda { |cmd, arg, result|
21
+ # Termtter::Plugin::Screen::set_title("termtter(#{public_storage[:unread_count]})")
22
+ # }
23
+ # )
24
+ # end
@@ -0,0 +1,14 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module Termtter::Client
4
+ register_command :name => :shell, :aliases => [:sh],
5
+ :help => ['shell,sh', 'Start your shell'],
6
+ :exec_proc => lambda {|args|
7
+ begin
8
+ pause
9
+ system ENV['SHELL'] || ENV['COMSPEC']
10
+ ensure
11
+ resume
12
+ end
13
+ }
14
+ end
data/lib/plugins/sl.rb ADDED
@@ -0,0 +1,48 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module Termtter
4
+
5
+ module Client
6
+
7
+ public_storage[:current] = ''
8
+ public_storage[:orig_prompt] = config.prompt
9
+ config.prompt = "~/ #{public_storage[:orig_prompt]}"
10
+
11
+ register_command(
12
+ :name => :sl, :aliases => [],
13
+ :exec_proc => lambda {|arg| system("sl") },
14
+ :help => ['sl', 'The train pass in front of your screen']
15
+ )
16
+
17
+ register_command(
18
+ :name => :pwd, :aliases => [],
19
+ :exec_proc => lambda {|arg| public_storage[:current] },
20
+ :help => ['pwd', 'Show current direcroty']
21
+ )
22
+
23
+ register_command(
24
+ :name => :ls, :aliases => [],
25
+ :exec_proc => lambda {|arg|
26
+ call_commands("list #{arg.empty? ? public_storage[:current] : arg}")
27
+ },
28
+ :completion_proc => lambda {|cmd, args|
29
+ find_user_candidates args, "#{cmd} %s"
30
+ },
31
+ :help => ['ls', 'Show list in current directory']
32
+ )
33
+
34
+ register_command(
35
+ :name => :cd, :aliases => [],
36
+ :exec_proc => lambda {|arg|
37
+ public_storage[:current] =
38
+ (arg.nil? || /\~/ =~ arg) ? '' : arg
39
+ config.prompt = "~/#{public_storage[:current]} #{public_storage[:orig_prompt]}"
40
+ },
41
+ :completion_proc => lambda {|cmd, args|
42
+ find_user_candidates args, "#{cmd} %s"
43
+ },
44
+ :help => ['cd USER', 'Change current directory']
45
+ )
46
+ end
47
+ end
48
+
@@ -0,0 +1,16 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ message = '*super spam time*'
4
+ Termtter::API.twitter.update(message)
5
+ puts "=> #{message}"
6
+
7
+ Termtter::Client.register_hook(
8
+ :name => :span,
9
+ :point => /^pre_exec/,
10
+ :exec => lambda{|*arg|
11
+ text = arg.join(' ')
12
+ Termtter::API.twitter.update(text)
13
+ puts "=> #{text}"
14
+ raise Termtter::CommandCanceled
15
+ }
16
+ )
@@ -0,0 +1,525 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require 'erb'
4
+ require 'set'
5
+
6
+ config.plugins.standard.set_default(
7
+ :limit_format,
8
+ '<<%=remaining_color%>><%=limit.remaining_hits%></<%=remaining_color%>>/<%=limit.hourly_limit%> until <%=limit.reset_time%> (<%=remaining_time%> remaining)')
9
+
10
+ module Termtter::Client
11
+
12
+ register_command(
13
+ :name => :update, :aliases => [:u],
14
+ :exec_proc => lambda {|arg|
15
+ unless arg.empty?
16
+ Termtter::API.twitter.update(arg)
17
+ puts "=> #{arg}"
18
+ end
19
+ },
20
+ :completion_proc => lambda {|cmd, args|
21
+ if /(.*)@([^\s]*)$/ =~ args
22
+ find_user_candidates $2, "#{cmd} #{$1}@%s"
23
+ end
24
+ },
25
+ :help => ["update,u TEXT", "Post a new message"]
26
+ )
27
+
28
+ direct_message_struct = Struct.new(:id, :text, :user, :created_at)
29
+ direct_message_struct.class_eval do
30
+ def method_missing(*args, &block)
31
+ nil
32
+ end
33
+ end
34
+ register_command(
35
+ :name => :direct, :aliases => [:d],
36
+ :exec_proc => lambda {|arg|
37
+ case arg
38
+ when /^([^\s]+)\s+(.*)\s*$/
39
+ user, text = $1, $2
40
+ Termtter::API.twitter.direct_message(user, text)
41
+ puts "=> to:#{user} message:#{text}"
42
+ when 'list'
43
+ output(
44
+ Termtter::API.twitter.direct_messages.map { |d|
45
+ direct_message_struct.new(d.id, "#{d.text} => #{d.recipient_screen_name}", d.sender, d.created_at)
46
+ },
47
+ :direct_messages
48
+ )
49
+ when 'sent_list'
50
+ output(
51
+ Termtter::API.twitter.sent_direct_messages.map { |d|
52
+ direct_message_struct.new(d.id, "#{d.text} => #{d.recipient_screen_name}", d.sender, d.created_at)
53
+ },
54
+ :direct_messages
55
+ )
56
+ end
57
+ },
58
+ :completion_proc => lambda {|cmd, arg|
59
+ find_user_candidates(arg, "#{cmd} %s") +
60
+ ["list", "sent_list"].grep(/^#{Regexp.quote(arg)}/).map { |i| "#{cmd} #{i}" }
61
+ },
62
+ :help => [
63
+ ["direct,d USERNAME TEXT", "Send direct message"],
64
+ ["direct,d list", 'List direct messages'],
65
+ ["direct,d sent_list", 'List sent direct messages']
66
+ ]
67
+ )
68
+
69
+ register_command(
70
+ :name => :profile, :aliases => [:p],
71
+ :exec_proc => lambda {|arg|
72
+ user = Termtter::API.twitter.user(arg.strip)
73
+ attrs = %w[ name screen_name url description profile_image_url location protected following
74
+ friends_count followers_count statuses_count favourites_count
75
+ id time_zone created_at utc_offset notifications
76
+ ]
77
+ label_width = attrs.map(&:size).max
78
+ attrs.each do |attr|
79
+ value = user.__send__(attr.to_sym)
80
+ puts "#{attr.gsub('_', ' ').rjust(label_width)}: #{value}"
81
+ end
82
+ },
83
+ :completion_proc => lambda {|cmd, arg|
84
+ find_user_candidates arg, "#{cmd} %s"
85
+ },
86
+ :help => ["profile,p USERNAME", "Show user's profile"]
87
+ )
88
+
89
+ register_command(
90
+ :name => :followers,
91
+ :exec_proc => lambda {|arg|
92
+ user_name = arg.strip
93
+ user_name = config.user_name if user_name.empty?
94
+
95
+ followers = []
96
+ page = 0
97
+ begin
98
+ followers += tmp = Termtter::API.twitter.followers(user_name, :page => page+=1)
99
+ end until tmp.empty?
100
+ Termtter::Client.public_storage[:followers] = followers
101
+ public_storage[:users] += followers.map(&:screen_name)
102
+ puts followers.map(&:screen_name).join(' ')
103
+ },
104
+ :completion_proc => lambda {|cmd, arg|
105
+ find_user_candidates arg, "#{cmd} %s"
106
+ },
107
+ :help => ["followers", "Show followers"]
108
+ )
109
+
110
+ register_command(
111
+ :name => :list, :aliases => [:l],
112
+ :exec_proc => lambda {|arg|
113
+ if arg.empty?
114
+ event = :list_friends_timeline
115
+ statuses = Termtter::API.twitter.friends_timeline
116
+ else
117
+ event = :list_user_timeline
118
+ statuses = Termtter::API.twitter.user_timeline(arg)
119
+ end
120
+ output(statuses, event)
121
+ },
122
+ :completion_proc => lambda {|cmd, arg|
123
+ find_user_candidates arg, "#{cmd} %s"
124
+ },
125
+ :help => ["list,l [USERNAME]", "List the posts"]
126
+ )
127
+
128
+ public_storage[:search_keywords] = Set.new
129
+ register_command(
130
+ :name => :search, :aliases => [:s],
131
+ :exec_proc => lambda {|arg|
132
+ statuses = Termtter::API.twitter.search(arg)
133
+ public_storage[:search_keywords] << arg
134
+ output(statuses, :search)
135
+ },
136
+ :completion_proc => lambda {|cmd, arg|
137
+ public_storage[:search_keywords].grep(/#{Regexp.quote(arg)}/).map { |i| "#{cmd} #{i}" }
138
+ },
139
+ :help => ["search,s TEXT", "Search for Twitter"]
140
+ )
141
+
142
+ register_command(
143
+ :name => :replies, :aliases => [:r],
144
+ :exec_proc => lambda {|arg|
145
+ output(Termtter::API.twitter.replies, :replies)
146
+ },
147
+ :help => ["replies,r", "List the most recent @replies for the authenticating user"]
148
+ )
149
+
150
+ register_command(
151
+ :name => :show,
152
+ :exec_proc => lambda {|arg|
153
+ id = arg.gsub(/.*:\s*/, '')
154
+ output([Termtter::API.twitter.show(id)], :show)
155
+ },
156
+ :completion_proc => lambda {|cmd, arg|
157
+ case arg
158
+ when /(\w+):\s*(\d+)\s*$/
159
+ find_status_ids($2).map{|id| "#{cmd} #{$1}: #{id}"}
160
+ else
161
+ users = find_users(arg)
162
+ unless users.empty?
163
+ users.map {|user| "#{cmd} #{user}:"}
164
+ else
165
+ find_status_ids(arg).map {|id| "#{cmd} #{id}"}
166
+ end
167
+ end
168
+ },
169
+ :help => ["show ID", "Show a single status"]
170
+ )
171
+
172
+ register_command(
173
+ :name => :shows,
174
+ :exec_proc => lambda {|arg|
175
+ id = arg.gsub(/.*:\s*/, '')
176
+ # TODO: Implement
177
+ #output([Termtter::API.twitter.show(id)], :show)
178
+ puts "Not implemented yet."
179
+ },
180
+ :completion_proc => get_command(:show).completion_proc
181
+ )
182
+
183
+ register_command(
184
+ :name => :follow, :aliases => [],
185
+ :exec_proc => lambda {|args|
186
+ args.split(' ').each do |arg|
187
+ if /^(\w+)/ =~ arg
188
+ res = Termtter::API::twitter.follow($1.strip)
189
+ end
190
+ end
191
+ },
192
+ :completion_proc => lambda {|cmd, args|
193
+ find_user_candidates args, "#{cmd} %s"
194
+ },
195
+ :help => ['follow USER', 'Follow user']
196
+ )
197
+
198
+ register_command(
199
+ :name => :leave, :aliases => [],
200
+ :exec_proc => lambda {|args|
201
+ args.split(' ').each do |arg|
202
+ if /^(\w+)/ =~ arg
203
+ res = Termtter::API::twitter.leave($1.strip)
204
+ end
205
+ end
206
+ },
207
+ :completion_proc => lambda {|cmd, args|
208
+ find_user_candidates args, "#{cmd} %s"
209
+ },
210
+ :help => ['leave USER', 'Leave user']
211
+ )
212
+
213
+ register_command(
214
+ :name => :favorite, :aliases => [:fav],
215
+ :exec_proc => lambda {|arg|
216
+ id = 0
217
+ case arg
218
+ when /^\d+/
219
+ id = arg.to_i
220
+ when /^@([A-Za-z0-9_]+)/
221
+ user = $1
222
+ statuses = Termtter::API.twitter.user_timeline(user)
223
+ return if statuses.empty?
224
+ id = statuses[0].id
225
+ when /^\/(.*)$/
226
+ word = $1
227
+ raise "Not implemented yet."
228
+ else
229
+ return
230
+ end
231
+
232
+ r = Termtter::API.twitter.favorite id
233
+ puts "Favorited status ##{r.id} on user @#{r.user.screen_name} #{r.text}"
234
+ },
235
+ :completion_proc => lambda {|cmd, arg|
236
+ case arg
237
+ when /@(.*)/
238
+ find_user_candidates $1, "#{cmd} @%s"
239
+ when /(\d+)/
240
+ find_status_ids(arg).map{|id| "#{cmd} #{id}"}
241
+ else
242
+ %w(favorite).grep(/^#{Regexp.quote arg}/)
243
+ end
244
+ },
245
+ :help => ['favorite,fav (ID|@USER|/WORD)', 'Favorite a status']
246
+ )
247
+
248
+ def self.show_settings(conf, level = 0)
249
+ conf.__values__.each do |k, v|
250
+ if v.instance_of? Termtter::Config
251
+ puts "#{k}:"
252
+ show_settings v, level + 1
253
+ else
254
+ print ' ' * level
255
+ puts "#{k} = #{v.nil? ? 'nil' : v.inspect}"
256
+ end
257
+ end
258
+ end
259
+
260
+ register_command(
261
+ :name => :settings, :aliases => [:set],
262
+ :exec_proc => lambda {|_|
263
+ show_settings config
264
+ },
265
+ :help => ['settings,set', 'Show your settings']
266
+ )
267
+
268
+ # TODO: Change colors when remaining_hits is low.
269
+ # TODO: Simmulate remaining_hits.
270
+ register_command(
271
+ :name => :limit, :aliases => [:lm],
272
+ :exec_proc => lambda {|arg|
273
+ limit = Termtter::API.twitter.limit_status
274
+ remaining_time = "%dmin %dsec" % (Time.parse(limit.reset_time) - Time.now).divmod(60)
275
+ remaining_color =
276
+ case limit.remaining_hits / limit.hourly_limit.to_f
277
+ when 0.2..0.4 then :yellow
278
+ when 0..0.2 then :red
279
+ else :green
280
+ end
281
+ erbed_text = ERB.new(config.plugins.standard.limit_format).result(binding)
282
+ puts TermColor.parse(erbed_text)
283
+ },
284
+ :help => ["limit,lm", "Show the API limit status"]
285
+ )
286
+
287
+ register_command(
288
+ :name => :pause,
289
+ :exec_proc => lambda {|arg| pause},
290
+ :help => ["pause", "Pause updating"]
291
+ )
292
+
293
+ register_command(
294
+ :name => :resume,
295
+ :exec_proc => lambda {|arg| resume},
296
+ :help => ["resume", "Resume updating"]
297
+ )
298
+
299
+ register_command(
300
+ :name => :exit, :aliases => [:e, :q, :quit],
301
+ :exec_proc => lambda {|arg| exit},
302
+ :help => ['exit,e', 'Exit']
303
+ )
304
+
305
+ register_command(
306
+ :name => :help, :aliases => [:h],
307
+ :exec_proc => lambda {|arg|
308
+ helps = []
309
+ @commands.map do |name, command|
310
+ next unless command.help
311
+ case command.help[0]
312
+ when String
313
+ helps << command.help
314
+ when Array
315
+ helps += command.help
316
+ end
317
+ end
318
+ helps.compact!
319
+ puts formatted_help(helps)
320
+ },
321
+ :help => ["help,h", "Print this help message"]
322
+ )
323
+
324
+ def self.formatted_help(helps)
325
+ helps = helps.sort_by {|help| help[0] }
326
+ width = helps.map {|n, _| n.size }.max
327
+ space = 3
328
+ helps.map {|name, desc|
329
+ name.to_s.ljust(width + space) + desc.to_s
330
+ }.join("\n")
331
+ end
332
+
333
+ # completion for standard commands
334
+
335
+ public_storage[:users] ||= Set.new
336
+ public_storage[:status_ids] ||= Set.new
337
+ public_storage[:last_status_id] ||= Hash.new
338
+
339
+ register_hook(
340
+ :name => :for_completion,
341
+ :points => [:pre_filter],
342
+ :exec_proc => lambda {|statuses, event|
343
+ statuses.each do |s|
344
+ public_storage[:users].add(s.user.screen_name)
345
+ public_storage[:users] += s.text.scan(/@([a-zA-Z_0-9]*)/).flatten
346
+ public_storage[:status_ids].add(s.id)
347
+ public_storage[:status_ids].add(s.in_reply_to_status_id) if s.in_reply_to_status_id
348
+ public_storage[:last_status_id].store(s.user.screen_name, s.id)
349
+ end
350
+ }
351
+ )
352
+
353
+ public_storage[:plugins] = (Dir["#{File.dirname(__FILE__)}/*.rb"] + Dir["#{Termtter::CONF_DIR}/plugins/*.rb"]).map do |f|
354
+ f.match(%r|([^/]+).rb$|)[1]
355
+ end
356
+
357
+ register_command(
358
+ :name => :plugin,
359
+ :exec_proc => lambda {|arg|
360
+ if arg.empty?
361
+ puts 'Should specify plugin name.'
362
+ return
363
+ end
364
+ begin
365
+ result = plugin arg
366
+ rescue LoadError
367
+ ensure
368
+ puts "=> #{result.inspect}"
369
+ end
370
+ },
371
+ :completion_proc => lambda {|cmd, args|
372
+ find_user_candidates args, "#{cmd} %s"
373
+ unless args.empty?
374
+ find_plugin_candidates args, "#{cmd} %s"
375
+ else
376
+ public_storage[:plugins].sort
377
+ end
378
+ },
379
+ :help => ['plugin FILE', 'Load a plugin']
380
+ )
381
+
382
+ register_command(
383
+ :name => :plugins,
384
+ :exec_proc => lambda {|arg|
385
+ puts public_storage[:plugins].sort.join("\n")
386
+ },
387
+ :help => ['plugins', 'Show list of plugins']
388
+ )
389
+
390
+ register_command(
391
+ :name => :reply,
392
+ :aliases => [:re],
393
+ :exec_proc => lambda {|arg|
394
+ case arg
395
+ when /^\s*(?:list|ls)\s*(?:\s+(\w+))?\s*$/
396
+ public_storage[:log4re] = if $1
397
+ public_storage[:log].
398
+ select{|l| l.user.screen_name == $1}.
399
+ sort {|a,b| a.id <=> b.id}
400
+ else
401
+ public_storage[:log].sort {|a,b| a.id <=> b.id}
402
+ end
403
+ public_storage[:log4re].each_with_index do |s, i|
404
+ puts "#{i}: #{s.user.screen_name}: #{s.text}"
405
+ end
406
+ when /^\s*(?:up(?:date)?)\s+(\d+)\s+(.+)$/
407
+ id = public_storage[:log4re][$1.to_i].id
408
+ text = $2
409
+ user = public_storage[:log4re][$1.to_i].user
410
+ update_with_user_and_id(text, user.screen_name, id) if user
411
+ public_storage.delete :log4re
412
+ when /^\s*(\d+)\s+(.+)$/
413
+ id, text = $1, $2
414
+ user = public_storage[:log].select {|l| l.id == id.to_i }.first.user
415
+ update_with_user_and_id(text, user.screen_name, id) if user
416
+ when /^\s*@(\w+)/
417
+ in_reply_to_status_id = Termtter::API.twitter.user($1).status.id rescue nil
418
+ params = in_reply_to_status_id ? {:in_reply_to_status_id => in_reply_to_status_id} : {}
419
+ Termtter::API.twitter.update(arg, params)
420
+ puts "=> #{arg}"
421
+ end
422
+ },
423
+ :completion_proc => lambda {|cmd, arg|
424
+ case arg
425
+ when /(.*)@([^\s]*)$/
426
+ find_user_candidates $2, "#{cmd} #{$1}@%s"
427
+ when /(\d+)/
428
+ find_status_ids(arg).map{|id| "#{cmd} #{id}"}
429
+ end
430
+ },
431
+ :help => ["reply,re @USERNAME or STATUS_ID", "Send a reply"]
432
+ )
433
+
434
+ register_command(
435
+ :name => :redo,
436
+ :aliases => [:"."],
437
+ :exec_proc => lambda {|arg|
438
+ break if Readline::HISTORY.length < 2
439
+ i = Readline::HISTORY.length - 2
440
+ input = ""
441
+ begin
442
+ input = Readline::HISTORY[i]
443
+ i -= 1
444
+ return if i <= 0
445
+ end while input == "redo" or input == "."
446
+ begin
447
+ Termtter::Client.call_commands(input)
448
+ rescue CommandNotFound => e
449
+ warn "Unknown command \"#{e}\""
450
+ warn 'Enter "help" for instructions'
451
+ rescue => e
452
+ handle_error e
453
+ end
454
+ },
455
+ :help => ["redo,.", "Execute previous command"]
456
+ )
457
+
458
+ def self.update_with_user_and_id(text, username, id)
459
+ text = ERB.new("@#{username} #{text}").result(binding).gsub(/\n/, ' ')
460
+ result = Termtter::API.twitter.update(text, {'in_reply_to_status_id' => id})
461
+ puts "=> #{text}"
462
+ result
463
+ end
464
+
465
+ =begin
466
+ = Termtter reply command
467
+ == Usage
468
+ === list
469
+ * ステータスリストを連番と一緒に出す。
470
+ > reply [list|ls]
471
+ 0: foo: foo's message
472
+ 1: bar: bar's message
473
+ ..
474
+
475
+ * ユーザ指定してリスト作成。
476
+ > reply [list|ls] foo
477
+ 0: foo: foo's message0
478
+ 1: foo: foo's message1
479
+
480
+ === reply
481
+ メッセージ送信の際、@usernameが自動的に付与される。
482
+
483
+ * status_idを自分で入力してreply送信
484
+ > reply 1234567890 message to status_id
485
+ => @foo message to status_id (reply to 1234567890)
486
+
487
+ * reply listコマンドで出したステータス番号に対してreply送信
488
+ > reply up 0 message to status no
489
+ => @foo message to status_no
490
+
491
+ * 対象ユーザの最後の発言に対してreply
492
+ > reply @foo message to foo
493
+ => @foo message to foo
494
+
495
+ == Todo
496
+ * 英語で説明 => ヘルプを設定する
497
+ * リファクタ
498
+ * 補完
499
+ * 確認画面を出したい
500
+ =end
501
+
502
+ def self.find_plugin_candidates(a, b)
503
+ public_storage[:plugins].
504
+ grep(/^#{Regexp.quote a}/i).
505
+ map {|u| b % u }
506
+ end
507
+
508
+ def self.find_status_ids(text)
509
+ public_storage[:status_ids].select {|id| /#{Regexp.quote(text)}/ =~ id.to_s}
510
+ end
511
+
512
+ def self.find_users(text)
513
+ public_storage[:users].select {|user| /^#{Regexp.quote(text)}/ =~ user}
514
+ end
515
+
516
+ def self.find_user_candidates(a, b)
517
+ users =
518
+ if a.nil? || a.empty?
519
+ public_storage[:users].to_a
520
+ else
521
+ public_storage[:users].grep(/^#{Regexp.quote a}/i)
522
+ end
523
+ users.map {|u| b % u }
524
+ end
525
+ end