termtter 1.7.2 → 1.8.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 (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
@@ -0,0 +1,12 @@
1
+ module Termtter::Client
2
+ register_command(
3
+ :name => :capital_update,
4
+ :author => 'ujihisa',
5
+ :alias => :cu,
6
+ :help =>[
7
+ 'capital_update, cu',
8
+ 'Posts a tweet all in captalized text.'],
9
+ :exec_proc => lambda {|arg|
10
+ execute('update ' + arg.upcase)
11
+ })
12
+ end
@@ -0,0 +1,149 @@
1
+ config.plugins.channel.set_default(:auto_reload_channels, {})
2
+ config.plugins.channel.set_default(:short_names, {})
3
+ config.plugins.channel.set_default(:colorize, true)
4
+ config.plugins.channel.set_default(:output_length, 7)
5
+ config.plugins.channel.set_default(:default_channel, :main)
6
+ config.plugins.channel.set_default(:channel_to_hash_proc, lambda {|c| c.to_i(36) })
7
+
8
+ # Channel spec
9
+ # /^@(.+)/ -- user_timeline of $1
10
+ # /^(.+)_s(earch)?$/ -- search result of $1
11
+ # /^replies$/ -- replies
12
+ # /^main$/ -- home_timeline
13
+ # otherwise -- list
14
+
15
+ # Extention Core
16
+ module Termtter
17
+ module API
18
+ class << self
19
+ def call_by_channel(c, *opt)
20
+ case c.to_s
21
+ when "main"
22
+ Termtter::API.twitter.home_timeline(*opt)
23
+ when "replies"
24
+ Termtter::API.twitter.replies(*opt)
25
+ when /^(.+)_s(earch)?$/
26
+ Termtter::API.twitter.search($1, *opt)
27
+ else
28
+ user_name, slug = c.to_s.split('/')
29
+ if !user_name.nil? && slug.nil?
30
+ slug = user_name
31
+ user_name = config.user_name
32
+ elsif user_name.empty?
33
+ user_name = config.user_name
34
+ end
35
+ user_name = Termtter::Client.normalize_as_user_name(user_name)
36
+ Termtter::API.twitter.list_statuses(user_name, slug, *opt)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ now_channel = config.plugins.channel.default_channel
44
+
45
+ Termtter::Client.register_command(
46
+ :name => :channel,
47
+ :alias => :c,
48
+ :help => ['channel, c', 'Show current channel or change channel'],
49
+ :author => 'Sora Harakami',
50
+ :exec => lambda {|arg|
51
+ if arg.empty?
52
+ puts "Current channel is #{now_channel}"
53
+ else
54
+ old = now_channel
55
+ now_channel = arg.to_sym
56
+ puts "Channel is tuned. #{old} => #{now_channel}"
57
+ @since_id = nil
58
+ end
59
+ }
60
+ )
61
+
62
+ Termtter::Client.register_command(
63
+ :name => :reload,
64
+ :author => 'Sora Harakami',
65
+ :exec => lambda {|arg|
66
+ # NOTE: Please edit here as well if reload command in lib/plugins/default/standard_commands.rb was edited.
67
+ args = @since_id ? [{:since_id => @since_id}] : []
68
+ statuses = Termtter::API.call_by_channel(now_channel, *args)
69
+ unless statuses.empty?
70
+ print "\e[0G" + "\e[K" unless win?
71
+ @since_id = statuses[0].id
72
+ Termtter::Client.output(statuses, Termtter::Event.new(:update_friends_timeline, :type => :main))
73
+ Readline.refresh_line if arg =~ /\-r/
74
+ end
75
+ },
76
+ :help => ['reload', 'Reload time line']
77
+ )
78
+
79
+ colorize_channel_cache = {}
80
+ Termtter::Client.register_hook(
81
+ :name => :add_channel_line, :point => :pre_output,
82
+ :author => 'Sora Harakami',
83
+ :exec => lambda {|t, e|
84
+ # Additional to channel
85
+ c =
86
+ case e[:type]
87
+ when :list, :lists
88
+ :"#{e[:list_user] == config.user_name ?
89
+ "" : e[:list_user]}/#{e[:list_slug]}"
90
+ when :user
91
+ :"@#{e[:user_name]}"
92
+ when :home_timeline, :main, :friends_timeline
93
+ :main
94
+ when :direct_message, :direct
95
+ :direct
96
+ when :search
97
+ :"#{e[:search_keyword]}_search"
98
+ when :reply, :replies
99
+ :replies
100
+ when :show
101
+ :show
102
+ when :favorite, :favorites
103
+ :favorite
104
+ when :multiple
105
+ :multiple
106
+ when :channel
107
+ e[:channel]
108
+ else
109
+ :unknown
110
+ end
111
+ # Add channel text to output text
112
+ otc = config.plugins.channel.short_names.key?(c) ?
113
+ config.plugins.channel.short_names[c] : c
114
+ ccolor = colorize_channel_cache.key?(otc) ?
115
+ colorize_channel_cache[otc] :
116
+ config.plugins.stdout.colors[
117
+ config.plugins.channel.channel_to_hash_proc.call(otc.to_s.gsub(/^\//, "")) %
118
+ config.plugins.stdout.colors.size]
119
+ colorize_channel_cache[otc] = ccolor
120
+ th = "#{config.plugin.channel.colorize ? "<#{ccolor}>":""}#{c.to_s.length > config.plugins.channel.output_length ?
121
+ otc.to_s[0, config.plugins.channel.output_length] : otc.to_s.rjust(config.plugins.channel.output_length)}#{config.plugin.channe.colorize ? "</#{ccolor}>":""}<90>| </90>"
122
+ th + t
123
+ }
124
+ )
125
+
126
+ # Add auto reloads
127
+ config.plugins.channel.auto_reload_channels.each do |c, i|
128
+ since_ids = {}
129
+ Termtter::Client.add_task(:name => "auto_reload_#{c}".to_sym, :interval => i) do
130
+ begin
131
+ if c != now_channel
132
+ # NOTE: Please edit here as well if reload command in lib/plugins/default/standard_commands.rb was edited.
133
+ args = since_ids[c] ? [{:since_id => since_ids[c]}] : []
134
+ statuses = Termtter::API.call_by_channel(c, *args)
135
+ unless statuses.empty?
136
+ print "\e[0G" + "\e[K" unless win?
137
+ since_ids[c] = statuses[0].id
138
+ Termtter::Client.output(statuses, Termtter::Event.new(:"update_#{c}", :type => :channel, :channel => c))
139
+ Readline.refresh_line
140
+ end
141
+ end
142
+ rescue TimeoutError
143
+ # do nothing
144
+ rescue Exception => e
145
+ Termtter::Client.handle_error(e)
146
+ end
147
+ end
148
+ end
149
+
@@ -0,0 +1,10 @@
1
+ module Termtter::Client
2
+ register_hook(
3
+ :name => :clock_prompt,
4
+ :point => :prepare_prompt,
5
+ :exec => lambda {|prompt|
6
+ time = Time.now.strftime('%I:%M%p').downcase
7
+ config.prompt = "#{time} #{public_storage[:orig_prompt]}"
8
+ }
9
+ )
10
+ end
@@ -32,6 +32,12 @@ module Termtter
32
32
  command_text = hook.call(command_text)
33
33
  }
34
34
  Client.execute(command_text)
35
+ rescue CommandNotFound => e
36
+ hooks = Client.get_hooks('command_not_found')
37
+ raise e if hooks.empty?
38
+ hooks.each {|hook|
39
+ hook.call(command_text)
40
+ }
35
41
  rescue TimeoutError
36
42
  puts TermColor.parse("<red>Time out :(</red>")
37
43
  end
@@ -106,6 +112,8 @@ module Termtter
106
112
  end
107
113
 
108
114
  def trap_setting()
115
+ return if /mswin(?!ce)|mingw|bccwin/ =~ RUBY_PLATFORM
116
+
109
117
  begin
110
118
  stty_save = `stty -g`.chomp
111
119
  trap("INT") do
@@ -1,5 +1,5 @@
1
1
  # -*- coding: utf-8 -*-
2
- config.set_default(:confirm, false)
2
+ config.set_default(:confirm, true)
3
3
 
4
4
  def confirm(message)
5
5
  if config.confirm && !Termtter::Client.confirm(message)
@@ -10,7 +10,7 @@ module Termtter::Client
10
10
  end
11
11
 
12
12
  register_command(:raw_update) do |args|
13
- temp = public_storage[:hashtags]
13
+ temp = public_storage[:hashtags].dup
14
14
  public_storage[:hashtags].clear
15
15
  execute "update #{args}"
16
16
  public_storage[:hashtags] = temp
@@ -33,6 +33,17 @@ module Termtter::Client
33
33
  text
34
34
  end
35
35
 
36
+ register_hook :notify_for_keywords, :point => :output do |statuses, event|
37
+ if event == :update_friends_timeline
38
+ regexp = Regexp.union(*public_storage[:keywords].map(&:to_s))
39
+ statuses.select { |status|
40
+ /#{regexp}/ =~ status.text
41
+ }.each do |status|
42
+ notify(status.user.screen_name, status.text)
43
+ end
44
+ end
45
+ end
46
+
36
47
  register_command(
37
48
  'keyword add',
38
49
  :help => ['keyword add KEYWORD', 'Add a highlight keyword']
@@ -15,7 +15,8 @@ module Termtter::Client
15
15
  register_command(
16
16
  :name => :list, :aliases => [:l],
17
17
  :exec_proc => lambda {|arg|
18
- if arg =~ /\-([\d]+)/
18
+ a = {}
19
+ if /\-([\d]+)/ =~ arg
19
20
  options = {:count => $1}
20
21
  arg = arg.gsub(/\-([\d]+)/, '')
21
22
  else
@@ -26,14 +27,18 @@ module Termtter::Client
26
27
  if arg.empty?
27
28
  event = :list_friends_timeline
28
29
  statuses = Termtter::API.twitter.home_timeline(options)
30
+ a[:type] = :home_timeline
29
31
  else
30
32
  event = :list_user_timeline
31
33
  statuses = []
32
34
  Array(arg.split).each do |user|
33
35
  if user =~ /\/\w+/
34
36
  user_name, slug = *user.split('/')
37
+ a[:type] = :list
35
38
  user_name = config.user_name if user_name.empty?
36
39
  user_name = normalize_as_user_name(user_name)
40
+ a[:list_user] = user_name
41
+ a[:list_slug] = slug
37
42
  options[:per_page] = options[:count]
38
43
  options.delete(:count)
39
44
  statuses += Termtter::API.twitter.list_statuses(user_name, slug, options)
@@ -47,6 +52,8 @@ module Termtter::Client
47
52
  end
48
53
  end
49
54
  user_name = normalize_as_user_name(user.sub(/\/$/, ''))
55
+ a[:type] = :user
56
+ a[:user_name] = user_name
50
57
  statuses += Termtter::API.twitter.user_timeline(user_name, options)
51
58
  rescue Rubytter::APIError => e
52
59
  last_error = e
@@ -54,7 +61,8 @@ module Termtter::Client
54
61
  end
55
62
  end
56
63
  end
57
- output(statuses, event)
64
+ a[:type] = :multiple if arg.split.length > 1
65
+ output(statuses, Termtter::Event.new(event, a))
58
66
  raise last_error if last_error
59
67
  },
60
68
  :help => ["list,l [USERNAME]/[SLUG] [-COUNT]", "List the posts"]
@@ -73,7 +81,7 @@ module Termtter::Client
73
81
  public_storage[:lists] += lists.map(&:full_name)
74
82
  puts lists.map{|i| i.full_name}.join("\n")
75
83
  },
76
- :help => ["lists [USERNAME]", "Show Lists"]
84
+ :help => ["list list [USERNAME]", "Show Lists"]
77
85
  )
78
86
 
79
87
  register_command(
@@ -92,7 +100,7 @@ module Termtter::Client
92
100
  end
93
101
  }
94
102
  },
95
- :help => ["list follow SLUG USERNAME", "Follow users to the list"]
103
+ :help => ["list follow|add LISTNAME USERNAME", "Follow users to the list"]
96
104
  )
97
105
 
98
106
  register_command(
@@ -110,7 +118,7 @@ module Termtter::Client
110
118
  end
111
119
  }
112
120
  },
113
- :help => ["list remove SLUG USERNAME", "Remove user(s) from the list"]
121
+ :help => ["list remove LISTNAME USERNAME", "Remove user(s) from the list"]
114
122
  )
115
123
 
116
124
  register_command(
@@ -146,7 +154,25 @@ module Termtter::Client
146
154
  end
147
155
  }
148
156
  },
149
- :help => ["list delete SLUG", "Delete list"]
157
+ :help => ["list delete LISTNAME", "Delete list"]
158
+ )
159
+
160
+ register_command(
161
+ :name => %s{list show},
162
+ :exec => lambda { |arg|
163
+ raise ArgumentError unless /([^\s]*\/[^\s]+)/ =~ arg
164
+ user_name, slug = *arg.split('/')
165
+ user_name = config.user_name if user_name.empty?
166
+ user_name = normalize_as_user_name(user_name)
167
+ list = Termtter::API.twitter.list(user_name, slug)
168
+ attrs = %w[ full_name slug description mode id member_count subscriber_count]
169
+ label_width = attrs.map(&:size).max
170
+ attrs.each do |attr|
171
+ value = list.__send__(attr.to_sym)
172
+ puts "#{attr.gsub('_', ' ').rjust(label_width)}: #{value}"
173
+ end
174
+ },
175
+ :help => ["list show LISTNAME", "Show the detail of list"]
150
176
  )
151
177
 
152
178
  def self.list_name_to_slug(list_name)
@@ -8,18 +8,21 @@ config.plugins.standard.set_default(
8
8
  '<<%=remaining_color%>><%=limit.remaining_hits%></<%=remaining_color%>>/<%=limit.hourly_limit%> until <%=Time.parse(limit.reset_time).getlocal%> (<%=remaining_time%> remaining)')
9
9
 
10
10
  config.set_default(:easy_reply, false)
11
+ config.plugins.standard.set_default(
12
+ :one_line_profile_format,
13
+ '<90>[<%=user_id%>]</90> <%= mark %> <<%=color%>><%= user.screen_name %>: <%= padding %><%= (user.description || "").gsub(/\r?\n/, "") %></<%=color%>>')
11
14
 
12
15
  module Termtter::Client
13
-
14
16
  register_command(
15
17
  :name => :reload,
16
18
  :exec => lambda {|arg|
19
+ # NOTE: If edit this command, please check and edit lib/plugins/channel.rb too, please.
17
20
  args = @since_id ? [{:since_id => @since_id}] : []
18
21
  statuses = Termtter::API.twitter.home_timeline(*args)
19
22
  unless statuses.empty?
20
23
  print "\e[0G" + "\e[K" unless win?
21
24
  @since_id = statuses[0].id
22
- output(statuses, :update_friends_timeline)
25
+ output(statuses, Termtter::Event.new(:update_friends_timeline, :type => :main))
23
26
  Readline.refresh_line if arg =~ /\-r/
24
27
  end
25
28
  },
@@ -33,8 +36,10 @@ module Termtter::Client
33
36
  params =
34
37
  if config.easy_reply && /^\s*(@\w+)/ =~ arg
35
38
  user_name = normalize_as_user_name($1)
36
- in_reply_to_status_id = Termtter::API.twitter.user(user_name).status.id rescue nil
37
- in_reply_to_status_id ? {:in_reply_to_status_id => in_reply_to_status_id} : {}
39
+ in_reply_to_status_id =
40
+ Termtter::API.twitter.user(user_name).status.id rescue nil
41
+ in_reply_to_status_id ?
42
+ {:in_reply_to_status_id => in_reply_to_status_id} : {}
38
43
  else
39
44
  {}
40
45
  end
@@ -77,7 +82,9 @@ module Termtter::Client
77
82
  end
78
83
  end
79
84
 
80
- register_command(:direct, :alias => :d, :help => ["direct,d USERNAME TEXT", "Send direct message"]) do |arg|
85
+ register_command(
86
+ :direct, :alias => :d,
87
+ :help => ["direct,d USERNAME TEXT", "Send direct message"]) do |arg|
81
88
  if /^([^\s]+)\s+?(.*)\s*$/ =~ arg
82
89
  user, text = normalize_as_user_name($1), $2
83
90
  Termtter::API.twitter.direct_message(user, text)
@@ -85,45 +92,93 @@ module Termtter::Client
85
92
  end
86
93
  end
87
94
 
88
- register_command('direct list', :help => ["direct,d list", 'List direct messages']) do |arg|
95
+ register_command(
96
+ 'direct list', :help => ["direct list", 'List direct messages']) do |arg|
89
97
  output(
90
98
  Termtter::API.twitter.direct_messages.map { |d|
91
99
  DirectMessage.new(d.id, "#{d.text} => #{d.recipient_screen_name}", d.sender, d.created_at)
92
100
  },
93
- :direct_messages
101
+ Termtter::Event.new(
102
+ :direct_messages,
103
+ :type => :direct_message)
94
104
  )
95
105
  end
96
106
 
97
- register_command('direct sent_list', :help => ["direct,d sent_list", 'List sent direct messages']) do |arg|
107
+ register_command(
108
+ 'direct sent_list',
109
+ :help => ["direct sent_list", 'List sent direct messages']) do |arg|
98
110
  output(
99
111
  Termtter::API.twitter.sent_direct_messages.map { |d|
100
- DirectMessage.new(d.id, "#{d.text} => #{d.recipient_screen_name}", d.sender, d.created_at)
112
+ DirectMessage.new(
113
+ d.id, "#{d.text} => #{d.recipient_screen_name}", d.sender, d.created_at)
101
114
  },
102
- :direct_messages
115
+ Termtter::Event.new(
116
+ :direct_messages,
117
+ :type => :direct_message)
103
118
  )
104
119
  end
105
120
 
121
+ def self.get_friends(user_name, max)
122
+ self.get_friends_or_followers(:followers, user_name, max)
123
+ end
124
+
125
+ def self.get_followers(user_name, max)
126
+ self.get_friends_or_followers(:followers, user_name, max)
127
+ end
128
+
129
+ def self.get_friends_or_followers(type, user_name, max)
130
+ raise "type should :friends or :followers" unless [:friends, :followers].include? type
131
+ users = []
132
+ cursor = -1
133
+ begin
134
+ tmp = Termtter::API::twitter.__send__(type, user_name, :cursor => cursor)
135
+ cursor = tmp[:next_cursor]
136
+ users += tmp[:users]
137
+ puts "#{users.length}/#{max}" if max > 100
138
+ rescue
139
+ break
140
+ end until (cursor.zero? or users.length > max)
141
+ users.take(max)
142
+ end
143
+
106
144
  register_command(
107
- :name => :followers,
145
+ :name => :friends, :aliases => [:following],
108
146
  :exec_proc => lambda {|arg|
109
- user_name = normalize_as_user_name(arg)
110
- user_name = config.user_name if user_name.empty?
147
+ friends_or_followers_command(:friends, arg)
148
+ },
149
+ :help => ["friends [USERNAME] [-COUNT]", "Show user's friends."]
150
+ )
111
151
 
112
- followers = []
113
- cursor = -1
114
- begin
115
- tmp = Termtter::API.twitter.followers(user_name, :cursor => cursor)
116
- cursor = tmp[:next_cursor]
117
- followers += tmp[:users]
118
- end until cursor.zero?
119
- Termtter::Client.public_storage[:followers] = followers
120
- public_storage[:users] += followers.map(&:screen_name)
121
- puts followers.map(&:screen_name).join(' ')
152
+ register_command(
153
+ :name => :followers,
154
+ :exec_proc => lambda {|arg|
155
+ friends_or_followers_command(:followers, arg)
122
156
  },
123
- :help => ["followers", "Show followers"]
157
+ :help => ["followers [USERNAME]", "Show user's followers."]
124
158
  )
125
159
 
126
- class SearchEvent; attr_reader :query; def initialize(query); @query = query end; end
160
+ def self.friends_or_followers_command(type, arg)
161
+ raise "type should :friends or :followers" unless [:friends, :followers].include? type
162
+ limit = 20
163
+ if /\-([\d]+)/ =~ arg
164
+ limit = $1.to_i
165
+ arg = arg.gsub(/\-([\d]+)/, '')
166
+ end
167
+ arg.strip!
168
+ user_name = arg.empty? ? config.user_name : arg
169
+ users = get_friends_or_followers(type, user_name, limit)
170
+ longest = users.map{ |u| u.screen_name.length}.max
171
+ users.reverse.each{|user|
172
+ padding = ' ' * (longest - user.screen_name.length)
173
+ user_id = Termtter::Client.data_to_typable_id(user.id) rescue ''
174
+ color = user.following ? 'BLACK' : 'RED'
175
+ mark = user.following ? '♥' : '✂'
176
+ erbed_text = ERB.new(config.plugins.standard.one_line_profile_format).result(binding)
177
+ puts TermColor.unescape(TermColor.parse(erbed_text))
178
+ }
179
+ end
180
+
181
+ class SearchEvent < Termtter::Event; attr_reader :query; def initialize(query); @query = query end; end
127
182
  public_storage[:search_keywords] = Set.new
128
183
  register_command(
129
184
  :name => :search, :aliases => [:s],
@@ -134,7 +189,7 @@ module Termtter::Client
134
189
  output(statuses, SearchEvent.new(arg))
135
190
  },
136
191
  :completion_proc => lambda {|cmd, arg|
137
- public_storage[:search_keywords].grep(/^#{Regexp.quote(arg)}/).map { |i| "#{cmd} #{i}" }
192
+ public_storage[:search_keywords].grep(/^#{Regexp.quote(arg)}/).map {|i| "#{cmd} #{i}" }
138
193
  },
139
194
  :help => ["search,s TEXT", "Search for Twitter"]
140
195
  )
@@ -142,7 +197,7 @@ module Termtter::Client
142
197
  case event
143
198
  when SearchEvent
144
199
  query = event.query.split(/\s/).map {|q|Regexp.quote(q)}.join("|")
145
- text.gsub(/(#{query})/i, '<on_magenta><white>\1</white></on_magenta>')
200
+ text.gsub(/#{query}/i, '<on_magenta><white>\0</white></on_magenta>')
146
201
  else
147
202
  text
148
203
  end
@@ -151,7 +206,7 @@ module Termtter::Client
151
206
  register_command(
152
207
  :name => :replies, :aliases => [:r],
153
208
  :exec_proc => lambda {|arg|
154
- if arg =~ /\-([\d]+)/
209
+ if /\-([\d]+)/ =~ arg
155
210
  options = {:count => $1}
156
211
  arg = arg.gsub(/\-([\d]+)/, '')
157
212
  else
@@ -160,18 +215,18 @@ module Termtter::Client
160
215
 
161
216
  res = Termtter::API.twitter.replies(options)
162
217
  unless arg.empty?
163
- res = res.map {|e| e.user.screen_name == arg ? e : nil }.compact
218
+ res = res.select {|e| e.user.screen_name == arg }
164
219
  end
165
- output(res, :replies)
220
+ output(res, Termtter::Event.new(:replies, :type => :reply))
166
221
  },
167
- :help => ["replies,r", "List the replies"]
222
+ :help => ["replies,r [username]", "List the replies (from the user)"]
168
223
  )
169
224
 
170
225
  register_command(
171
226
  :name => :show,
172
227
  :exec_proc => lambda {|arg|
173
228
  id = arg.gsub(/.*:\s*/, '')
174
- output([Termtter::API.twitter.show(id)], :show)
229
+ output([Termtter::API.twitter.show(id)], Termtter::Event.new(:show, :type => :show))
175
230
  },
176
231
  :completion_proc => lambda {|cmd, arg|
177
232
  case arg
@@ -205,26 +260,52 @@ module Termtter::Client
205
260
  :exec_proc => lambda {|args|
206
261
  args.split(' ').each do |arg|
207
262
  user_name = normalize_as_user_name(arg)
208
- res = Termtter::API::twitter.follow(user_name)
263
+ user = Termtter::API::twitter.follow(user_name)
264
+ puts "followed #{user.name}"
209
265
  end
210
266
  },
211
267
  :help => ['follow USER', 'Follow user']
212
268
  )
213
269
 
214
270
  register_command(
215
- :name => :leave, :aliases => [],
271
+ :name => :leave, :aliases => [:remove],
216
272
  :exec_proc => lambda {|args|
217
273
  args.split(' ').each do |arg|
218
274
  user_name = normalize_as_user_name(arg)
219
- res = Termtter::API::twitter.leave(user_name)
275
+ user = Termtter::API::twitter.leave(user_name)
276
+ puts "leaved #{user.name}"
220
277
  end
221
278
  },
222
279
  :help => ['leave USER', 'Leave user']
223
280
  )
224
281
 
282
+ register_command(
283
+ :name => :block, :aliases => [],
284
+ :exec_proc => lambda {|args|
285
+ args.split(' ').each do |arg|
286
+ user_name = normalize_as_user_name(arg)
287
+ user = Termtter::API::twitter.block(user_name)
288
+ puts "blocked #{user.name}"
289
+ end
290
+ },
291
+ :help => ['block USER', 'Block user']
292
+ )
293
+
294
+ register_command(
295
+ :name => :unblock, :aliases => [],
296
+ :exec_proc => lambda {|args|
297
+ args.split(' ').each do |arg|
298
+ user_name = normalize_as_user_name(arg)
299
+ user = Termtter::API::twitter.unblock(user_name)
300
+ puts "unblocked #{user.name}"
301
+ end
302
+ },
303
+ :help => ['unblock USER', 'Unblock user']
304
+ )
305
+
225
306
  help = ['favorite_list USERNAME', 'show user favorites']
226
307
  register_command(:favorites, :alias => :favlist, :help => help) do |arg|
227
- output Termtter::API.twitter.favorites(arg), :user_timeline
308
+ output Termtter::API.twitter.favorites(arg), :user_timeline, :type => :favorite
228
309
  end
229
310
 
230
311
  register_command(
@@ -372,15 +453,15 @@ module Termtter::Client
372
453
  end
373
454
  },
374
455
  :completion_proc => lambda {|cmd, args|
375
- plugin_list.grep(/^#{Regexp.quote(args)}/).map {|i| "#{cmd} #{i}"}
456
+ plugin_list.grep(/#{Regexp.quote(args)}/).map {|i| "#{cmd} #{i}"}
376
457
  },
377
458
  :help => ['plug FILE', 'Load a plugin']
378
459
  )
379
460
 
380
461
  ## plugin_list :: IO ()
381
462
  def self.plugin_list
382
- (Dir["#{File.dirname(__FILE__)}/../*.rb"] + Dir["#{Termtter::CONF_DIR}/plugins/*.rb"]).
383
- map {|f| File.basename(f).sub(/\.rb$/, '')}.
463
+ (Dir["#{File.dirname(__FILE__)}/../**/*.rb"] + Dir["#{Termtter::CONF_DIR}/plugins/**/*.rb"]).
464
+ map {|f| File.expand_path(f).scan(/.*plugins\/(.*)\.rb/).flatten[0] }.
384
465
  sort
385
466
  end
386
467
 
@@ -455,21 +536,23 @@ module Termtter::Client
455
536
  :help => ["redo,.", "Execute previous command"]
456
537
  )
457
538
 
458
- def self.update_with_user_and_id(text, username, id)
459
- text = "@#{username} #{text}"
460
- result = Termtter::API.twitter.update(text, {'in_reply_to_status_id' => id})
461
- puts "replied => #{result.text}"
462
- end
539
+ class << self
540
+ def update_with_user_and_id(text, username, id)
541
+ text = "@#{username} #{text}"
542
+ result = Termtter::API.twitter.update(text, {'in_reply_to_status_id' => id })
543
+ puts "replied => #{result.text}"
544
+ end
463
545
 
464
- def self.normalize_as_user_name(text)
465
- text.strip.sub(/^@/, '')
466
- end
546
+ def normalize_as_user_name(text)
547
+ text.strip.sub(/^@/, '')
548
+ end
467
549
 
468
- def self.find_status_ids(text)
469
- public_storage[:status_ids].select {|id| /#{Regexp.quote(text)}/ =~ id.to_s}
470
- end
550
+ def find_status_ids(text)
551
+ public_storage[:status_ids].select {|id| /#{Regexp.quote(text)}/ =~ id.to_s }
552
+ end
471
553
 
472
- def self.find_users(text)
473
- public_storage[:users].select {|user| /^#{Regexp.quote(text)}/ =~ user}
554
+ def find_users(text)
555
+ public_storage[:users].select {|user| /^#{Regexp.quote(text)}/ =~ user }
556
+ end
474
557
  end
475
558
  end